14

Rust を使用して子シェルを生成し、任意のコマンドを繰り返し渡してその出力を処理できるようにしたいと考えています。単一のコマンドを渡し、その単一の出力を受け取る方法を示す多くの例をオンラインで見つけましたが、繰り返し実行できないようです。

たとえば、次のコードはコメントの後の行でハングします。(read_to_string()子プロセスからstdoutを受け取るまでブロックしていると思いますが、もしそうなら、なぜその出力が来ないのか理解できません..)

let mut child_shell = match Command::new("/bin/bash")
    .stdin(Stdio::piped())
    .stdout(Stdio::piped())
    .spawn()
{
    Err(why) => panic!("couldn't spawn child_shell: {}", Error::description(&why)),
    Ok(process) => process,
};

loop {
    {
        match child_shell.stdin.as_mut().unwrap().write("ls".as_bytes()) {
            Err(why) => panic!(
                "couldn't send command to child shell: {}",
                Error::description(&why)
            ),
            Ok(_) => println!("sent command to child shell"),
        }
    }

    {
        let mut s = String::new();
        // ↓ hangs on this line ↓
        match child_shell.stdout.as_mut().unwrap().read_to_string(&mut s) {
            Err(why) => panic!("couldn't read bash stdout: {}", Error::description(&why)),
            Ok(_) => print!("bash responded with:\n{}", s),
        }
    }
}

私は Rust の初心者であり、コードからループ命令を削除して参照を構造体のstd::process::Child内部を不変にする。たとえば、これから:

child_shell.stdin.as_mut().unwrap().write("ls".as_bytes())

これに:

 child_shell.stdin.unwrap().write("ls".as_bytes())

明らかに、繰り返し実行するlsことは私の最終的な目標ではありません。シェル スクリプトを作成して、それを Rust に繰り返し実行させることもできますが、(Rust についてもっと学ぶという目標は別として!)これは私が必要とするものです。少なくとも原則として、より複雑なプロジェクトで実行できるようにする必要があります (それが何らかの解決策に関連していることが判明した場合は喜んで参加しますが、おそらくこの質問の範囲外です!)

最後に、この方法で子シェルを使用できないことが判明した場合でも、他の任意のコマンドを実行している生成されたプロセスとの間で繰り返し/継続的にパイプする方法を学びたいと思います。 Rust のドキュメント、チュートリアル、またはスタック オーバーフローで情報を見つけることができません。

4

1 に答える 1

17

read_to_stringとして文書化されています

このソースの EOF までのすべてのバイトを読み取る

したがって、すべての入力が完了するまで待機しますが、これはシェルが閉じられるまで発生しません。これは、出力から一定量のデータを読み取ることで修正できます。これは、ソリューションのコアを表示するために必要なすべての適切なエラー出力を削除した例です。

use std::process::{Command, Stdio};
use std::io::{BufRead, Write, BufReader};

fn main() {
    let mut child_shell = Command::new("/bin/bash")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn()
        .unwrap();

    let child_in = child_shell.stdin.as_mut().unwrap();
    let mut child_out = BufReader::new(child_shell.stdout.as_mut().unwrap());
    let mut line = String::new();

    loop {
        child_in.write("ls\n".as_bytes()).unwrap();
        child_out.read_line(&mut line).unwrap();
        println!("{}", line);
    }
}

ここでは、BufReadトレイトを使用して、1 行分の読み取りが完了するまで入力からの読み取りを許可します。それを印刷して、ループを続けます。もちろん、入力行ごとに複数行の出力があるため、読み取り待ちの行がどんどん増えていきます。

実際のコードでは、読み取りを停止するタイミングを把握する必要があります。これは、固定サイズの応答がある場合は非常に簡単ですが、人間と対話するプログラムを処理しようとしている場合は非常に困難です。

またはを使用せずにchild_shell.stdinorstdoutを直接使用する場合は注意してください。orを直接使用すると、そのアイテムが構造の外に移動し、部分的に有効なままになります。たとえば、電話をかけたり、電話をかけたりすることはできなくなります。Option::as_refOption::as_mutOption::takestdinstdoutChildChildwaitkill

関係のない話ですが、 のようなトレイト メソッドを呼び出す必要はありませんError::description(&why)。あなたはただ言うことができますwhy.description()

于 2015-07-23T02:41:18.660 に答える