外部命令
运行外部命令,并处理 stdout
把git log --oneline
,作为一个外部Command
,并检查其Output
,和用Regex
获取最后 5 次提交的哈希与消息。
# #[macro_use] # extern crate error_chain; extern crate regex; use std::process::Command; use regex::Regex; # # error_chain!{ # foreign_links { # Io(std::io::Error); # Regex(regex::Error); # Utf8(std::string::FromUtf8Error); # } # } #[derive(PartialEq, Default, Clone, Debug)] struct Commit { hash: String, message: String, } fn run() -> Result<()> { let output = Command::new("git").arg("log").arg("--oneline").output()?; if !output.status.success() { bail!("Command executed with failing error code"); } let pattern = Regex::new(r"(?x) ([0-9a-fA-F]+) # commit hash (.*) # The commit message")?; String::from_utf8(output.stdout)? .lines() .filter_map(|line| pattern.captures(line)) .map(|cap| { Commit { hash: cap[1].to_string(), message: cap[2].trim().to_string(), } }) .take(5) .for_each(|x| println!("{:?}", x)); Ok(()) } # # quick_main!(run);
运行传递 stdin 的外部命令,并检查错误代码
使用外部Command
,打开python
解释程序,并将 python (语言)语句传递给它,以供执行。之后的输出Output
会被解析。
# #[macro_use] # extern crate error_chain; # use std::collections::HashSet; use std::io::Write; use std::process::{Command, Stdio}; # # error_chain!{ # errors { CmdError } # foreign_links { # Io(std::io::Error); # Utf8(std::string::FromUtf8Error); # } # } fn run() -> Result<()> { let mut child = Command::new("python").stdin(Stdio::piped()) .stderr(Stdio::piped()) .stdout(Stdio::piped()) .spawn()?; child.stdin .as_mut() .ok_or("Child process stdin has not been captured!")? .write_all(b"import this; copyright(); credits(); exit()")?; let output = child.wait_with_output()?; if output.status.success() { let raw_output = String::from_utf8(output.stdout)?; let words = raw_output.split_whitespace() .map(|s| s.to_lowercase()) .collect::<HashSet<_>>(); println!("Found {} unique words:", words.len()); println!("{:#?}", words); Ok(()) } else { let err = String::from_utf8(output.stderr)?; bail!("External command failed:\n {}", err) } } # # quick_main!(run);
运行管道的外部命令
显示,当前工作目录中,10个最大的文件和子目录。它相当于运行:du -ah . | sort -hr | head -n 10
。
Command
们代表一个过程。一个子进程的输出用Stdio::piped
,在父级和子级之间捕获。
# #[macro_use] # extern crate error_chain; # use std::process::{Command, Stdio}; # # error_chain! { # foreign_links { # Io(std::io::Error); # Utf8(std::string::FromUtf8Error); # } # } fn run() -> Result<()> { let directory = std::env::current_dir()?; let mut du_output_child = Command::new("du") .arg("-ah") .arg(&directory) .stdout(Stdio::piped()) .spawn()?; if let Some(du_output) = du_output_child.stdout.take() { let mut sort_output_child = Command::new("sort") .arg("-hr") .stdin(du_output) .stdout(Stdio::piped()) .spawn()?; du_output_child.wait()?; if let Some(sort_output) = sort_output_child.stdout.take() { let head_output_child = Command::new("head") .args(&["-n", "10"]) .stdin(sort_output) .stdout(Stdio::piped()) .spawn()?; let head_stdout = head_output_child.wait_with_output()?; sort_output_child.wait()?; println!( "Top 10 biggest files and directories in '{}':\n{}", directory.display(), String::from_utf8(head_stdout.stdout).unwrap() ); } } Ok(()) } # # quick_main!(run);
将子进程的 stdout 和 stderr ,重定向到同一文件
生成(Spawns)一个子进程,并重定向stdout
和stderr
到同一个文件。它与运行管道的外部命令差不多的想法,但会用process::Stdio
,把输出写入到指定的文件。File::try_clone
引用相同文件的stdout
和stderr
控制(Handle)。它将确保两个 Handles 使用相同的光标位置写入。
下面的食谱,相当于运行 unix shell 命令:ls . oops >out.txt 2>&1
.
use std::fs::File; use std::io::Error; use std::process::{Command, Stdio}; fn main() -> Result<(), Error> { let outputs = File::create("out.txt")?; let errors = outputs.try_clone()?; Command::new("ls") .args(&[".", "oops"]) .stdout(Stdio::from(outputs)) .stderr(Stdio::from(errors)) .spawn()? .wait_with_output()?; Ok(()) }
连续处理,子进程的输出
在运行外部命令,并处理 stdout食谱中,stdout
的处理会在外部Command
完成之后执行。而本食谱接下来的方法是,调用Stdio::piped
创建一个管道,并在BufReader
每次更新,都读取stdout
(连续)。
下面的食谱,相当于 unix shell 命令:journalctl | grep usb
.
use std::process::{Command, Stdio}; use std::io::{BufRead, BufReader, Error, ErrorKind}; fn main() -> Result<(), Error> { let stdout = Command::new("journalctl") .stdout(Stdio::piped()) .spawn()? .stdout .ok_or_else(|| Error::new(ErrorKind::Other,"Could not capture standard output."))?; let reader = BufReader::new(stdout); reader .lines() .filter_map(|line| line.ok()) .filter(|line| line.find("usb").is_some()) .for_each(|line| println!("{}", line)); Ok(()) }