无阻塞地读取 Childstdout

Read Childstdout without blocking

我正在尝试重现 Shepmasters 对 的回答,但出现以下编译错误。

error[E0599]: the method `for_each` exists for struct `tokio::io::Lines<tokio::io::BufReader<tokio::process::ChildStdout>>`, but its trait bounds were not satisfied
  --> src/main.rs:19:10
   |
19 |           .for_each(|s| async move { println!("> {:?}", s) })
   |            ^^^^^^^^ method cannot be called on `tokio::io::Lines<tokio::io::BufReader<tokio::process::ChildStdout>>` due to unsatisfied trait bounds
   | 
  ::: /home/.../tokio-1.7.1/src/io/util/lines.rs:10:1
   |
10 | / pin_project! {
11 | |     /// Read lines from an [`AsyncBufRead`].
12 | |     ///
13 | |     /// A `Lines` can be turned into a `Stream` with [`LinesStream`].
...  |
29 | |     }
30 | | }
   | |_- doesn't satisfy `_: Iterator`
   |
   = note: the following trait bounds were not satisfied:
           `tokio::io::Lines<tokio::io::BufReader<tokio::process::ChildStdout>>: Iterator`
           which is required by `&mut tokio::io::Lines<tokio::io::BufReader<tokio::process::ChildStdout>>: Iterator`

这是我的代码(Raspberry Pi 4 上的 rust 1.52.1)

[dependencies]
futures = "0.3.15"
tokio = { version = "1", features = ["full"] }
use futures::StreamExt; // <-- claimed to be unused
use std::process::Stdio; 
// use tokio::prelude::*;  <-- doesn't exit
use tokio::{io::AsyncBufReadExt, io::BufReader, process::Command};

#[tokio::main]
async fn main() {
    let mut child = Command::new("sudo")
        .arg("ls")
        .stdout(Stdio::piped())
        .spawn()
        .expect("Command failed");

    let mut stdout = child.stdout.take().unwrap();

    BufReader::new(stdout)
        .lines()
        .for_each(|s| async move { println!("> {:?}", s) })
        .await;
}

更一般地说,我如何才能更好地学习我需要导入的特征?在其他语言中,我只会查看给定 class 上的方法,但在 rust 中,我很难发现必要的特征。例如。映射 Futures 我花了好几天才找到 futures::FutureExt.

我喜欢 Tokio 文档中的这个 example

下面是稍作修改以打印所有 stderr 行的代码:

use tokio::io::{BufReader, AsyncBufReadExt};
use tokio::process::Command;
use std::process::{Stdio};

    async fn run_command(shell_cmd_str: &str) -> Result<()> {
        let mut cmd = Command::new("sh");
        cmd.args(&["-c", shell_cmd_str]);
        cmd.stderr(Stdio::piped());
    
        let mut child = cmd.spawn()
            .expect("failed to spawn command");
    
        let stderr = child.stderr.take()
            .expect("child did not have a handle to stdout");
    
        let mut stderr_reader = BufReader::new(stderr).lines();
    
        tokio::spawn(async {
            let status = child.await
                .expect("child process encountered an error");
            println!("child status was: {}", status);
        });
    
        while let Some(line) = stderr_reader.next_line().await? {
            println!("Stderr line: {}", line);
        }
    
    
        Ok(())
    }