【问题标题】:How to capture the output of a process piped into a Rust program?如何捕获通过管道传输到 Rust 程序的进程的输出?
【发布时间】:2018-04-09 12:30:40
【问题描述】:

我知道如何读取命令行参数,但我在读取管道的命令输出时遇到了困难。

  1. 使用管道将输出数据的程序 (A) 连接到我的 Rust 程序:

    A | R
    
  2. 程序应该一行一行地消耗数据。

    $ pwd | cargo run 应该打印 pwd 输出。

    $ find . | cargo run 应该输出超过 1 行的find 命令输出。

【问题讨论】:

    标签: rust pipe rust-cargo


    【解决方案1】:

    locked handle to standard input 上使用BufRead::lines

    use std::io::{self, BufRead};
    
    fn main() {
        let stdin = io::stdin();
        for line in stdin.lock().lines() {
            let line = line.expect("Could not read line from standard in");
            println!("{}", line);
        }
    }
    

    如果你想重用String的分配,你可以使用循环形式:

    use std::io::{self, Read};
    
    fn main() {
        let stdin = io::stdin();
        let mut stdin = stdin.lock(); // locking is optional
    
        let mut line = String::new();
    
        // Could also `match` on the `Result` if you wanted to handle `Err` 
        while let Ok(n_bytes) = stdin.read_to_string(&mut line) {
            if n_bytes == 0 { break }
            println!("{}", line);
            line.clear();
        }
    }
    

    【讨论】:

    • 如果我理解正确lock(),这将破坏管道的用途,find 会将其输出写入流中,但您当前处于锁定状态并仅读取 find 已经读取的内容。带有read_line()while 循环不会更加惯用和实用吗?
    • @Stargateur 不正确。 lock 意味着进程中的任何其他内容都不能从 STDIN 读取,而不是它会停止从进程外部读取数据。这比其他地方建议的loop 具有更高的性能(但不是最快的可能性),并且迭代器非常惯用。
    • grep 这样的工具在单独调用时不会锁定。它是如何工作的?
    【解决方案2】:

    您只需要阅读Stdin

    这是基于取自the documentation的示例:

    use std::io;
    
    fn main() {
        loop {
            let mut input = String::new();
            match io::stdin().read_line(&mut input) {
                Ok(len) => if len == 0 {
                    return;
                } else {
                    println!("{}", input);
                } 
                Err(error) => {
                    eprintln!("error: {}", error);
                    return;
                }
            }
        }
    }
    

    它主要是包裹在一个循环中的文档示例,当没有更多输入或出现错误时退出循环。

    其他更改是在您的上下文中最好将错误写入stderr,这就是错误分支使用eprintln! 而不是println! 的原因。编写该文档时,此宏可能不可用。

    【讨论】:

    • 感谢彼得,我在发布此问题后确实发现了这一点,但这无法读取大于 1 行的输出。
    • 您可以在循环中执行此操作,并在行长度为零时退出。我会更新我的答案。
    【解决方案3】:
    use std::io;
    
    fn main() {
        loop {
            let mut input = String::new();
            io::stdin()
                .read_line(&mut input)
                .expect("failed to read from pipe");
            input = input.trim().to_string();
            if input == "" {
                break;
            }
            println!("Pipe output: {}", input);
        }
    }
    

    输出:

    [18:50:29 Abhinickz@wsl -> pipe$ pwd
    /mnt/d/Abhinickz/dev_work/learn_rust/pipe
    [18:50:46 Abhinickz@wsl -> pipe$ pwd | cargo run
        Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
        Running `target/debug/pipe`
    Pipe output: /mnt/d/Abhinickz/dev_work/learn_rust/pipe
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-08-19
      • 1970-01-01
      • 2018-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多