无法管道到或从生成的子进程多次 [英] Unable to pipe to or from spawned child process more than once

查看:243
本文介绍了无法管道到或从生成的子进程多次的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想能够使用Rust生成一个子shell,然后重复地传递任意命令并处理它们的输出。我在网上找到了很多例子,告诉我如何传递一个命令并接收它的单个输出,但我似乎不能一再做到。



例如,以下代码在注释后的行上挂起。 (我想可能 read_to_string()是阻塞,直到它从子进程接收stdout,但如果是这样,我不明白为什么该输出不是即将到来。)

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

loop {

{
match child_shell.stdin.as_mut()。unwrap()。write(ls.as_bytes())
{
Err(why)=> panic!(无法向子shell发送命令:{},Error :: description(& why)),
Ok(_)=> println!(发送命令到子shell),
}
}

{
let mut s = String :: new();
//↓挂在这一行↓
匹配child_shell.stdout.as_mut()。unwrap()。read_to_string(& mut s);
{
Err(why)=> panic!(无法读取bash stdout:{},Error :: description(& why)),
Ok(_)=> print!(bash respond with:\\\
{},s),
}
}

}
pre>

我是Rust的初学者,我认为问题是我对借书检查/引用规则的理解有限,因为上述运行良好迭代)如果我从代码中删除循环指令,并将对 std :: process :: Child struct的内部的引用更改为immutable;例如从这里:

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

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


$ b b

显然,重复运行 ls 不是我的最终目标,我知道我可以写一个shell脚本,然后Rust反复运行它 - 但是除了只是学习更多关于Rust的目标),这是我需要能够做,至少在原则上,一个更复杂的项目(我很高兴进入,如果它可能证明与任何解决方案相关,但是它可能是方式,方式超出了这个问题的范围!)



最后,如果事实证明,这是不可能使用子shell ,我仍然想学习如何反复/连续地从一个产生的进程运行一些其他任意命令,因为我不能在Rust文档,教程或Stack Overflow中找到任何信息。

解决方案

read_to_string 记录为


在此源中读取所有字节,直到EOF


因此,它会等待所有输入完成,壳被关闭。您可以通过从输出中读取一定量的数据来解决此问题。这里有一个例子,我删除所有的错误打印你必须显示解决方案的核心:

 使用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\\\
.as_bytes())。unwrap();
child_out.read_line(& mut line).unwrap();
println!({},line);
}
}

在这里,我们使用 BufRead trait允许从输入读取,直到我们读取一行值。然后我们打印出来,继续我们的循环。当然,每行输入有多行输出,所以这只会有越来越多的等待读取。



在你的实际代码中,你将需要弄清楚什么时候停止阅读。如果你有固定大小的反应,这可能很容易,或如果你想处理一个人性化的交互式程序真的很难。



小心使用 child_shell.stdin stdout 直接,没有 as_ref / as_mut 。直接使用这些会将该项移出 Child 结构,使 Child 部分有效。例如,您将无法再调用 wait kill



在一个不相关的注释中,你不需要调用trait方法,如 Error :: description(& why)。您只需说 why.description()


I want to be able to use Rust to spawn a child shell, then repeatedly pass it arbitrary commands and process their outputs. I have found plenty of examples online showing me how to pass a single command and receive its single output, but I can't seem to be able to do it repeatedly.

For instance, the following code hangs on the line after the comment. (I imagine maybe read_to_string() is blocking until it receives stdout from the child process, but if so I don't understand why that output isn't forthcoming..)

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),
        }
    }

}

I'm a beginner in Rust and I think the problem is my limited understanding of the borrow-checker/referencing rules, since the above runs fine (for a single iteration) if I remove the loop instruction from the code and change the references to the innards of the std::process::Child struct to immutable; for instance from this:

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

to this:

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

Obviously, repeatedly running ls isn't my ultimate goal, and I know that I could just write a shell script and then have Rust repeatedly run it - but (apart from the goal of just learning more about Rust!) this is something I need to be able to do, at least in principle, for a more complicated project (which I'm happy to go into if it might prove relevant to any solutions, but it's probably way, way outside the scope of this question!)

Finally, if it turns out that it isn't possible to use a child shell in this way, I'd nevertheless like to learn how to repeatedly/continuously pipe to and from a spawned process running some other arbitrary command, as I wasn't able to find any info in the Rust documentation, tutorials or on Stack Overflow.

解决方案

read_to_string is documented as

Read all bytes until EOF in this source

Thus, it is waiting until all the input is done, which will never happen until the shell is closed. You can fix this by reading a set amount of data from the output. Here's an example where I removed all the nice error printing you had to show the core of the solution:

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);
    }
}

Here, we use the BufRead trait to allow reading from the input until we have read one line worth. We then print that out and continue on our loop. Of course, there's more than one line of output per line of input, so this will just have more and more waiting to be read.

In your real code, you will need to have to figure out when to stop reading. This could be really easy if you have fixed-size responses, or really hard if you are trying to deal with a human-interactive program.

Be careful about using child_shell.stdin or stdout directly, without an as_ref / as_mut. Using these directly will move that item out of the Child structure, leaving the Child partially valid. You would no longer be able to call wait or kill on it for example.

On an unrelated note, you don't need to call trait methods like Error::description(&why). You can just say why.description().

这篇关于无法管道到或从生成的子进程多次的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆