1. 外部命令
需要安装regex
库和error-chain
库,可通过cargo add regex
和cargo add error-chain
命令安装
[dependencies] regex = "1.8.1" error-chain = "0.12.4"
|
1.1 运行外部命令并处理 stdout
将 git log --oneline
作为外部命令 Command
运行,并使用 Regex
检查其 Output
,以获取最后 5 次提交的哈希值和消息。
use error_chain::error_chain;
use regex::Regex; use std::process::Command;
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 main() -> Result<()> { let output = Command::new("git").arg("log").arg("--oneline").output()?;
if !output.status.success() { error_chain::bail!("Command executed with failing error code"); }
let pattern = Regex::new( r"(?x) ([0-9a-fA-F]+) # 提交的哈希值 (.*) # 提交信息", )?;
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(()) }
|
Commit { hash: "f307002", message: "test11" } Commit { hash: "60d4e16", message: "test12" } Commit { hash: "7335ab9", message: "test13" } Commit { hash: "e545eef", message: "test14" } Commit { hash: "9ed33ac", message: "test15" }
|
1.2 运行传递 stdin
的外部命令,并检查错误代码
使用外部命令 Command
打开 python
(注:Linux下可能是python3
) 解释器,并传递一条 python
语句供其执行,然后解析语句的输出结构体 Output
use error_chain::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 main() -> Result<()> { let mut child = Command::new("python3") .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!("找到 {} 个独特的词:", words.len()); println!("{:#?}", words); Ok(()) } else { let err = String::from_utf8(output.stderr)?; error_chain::bail!("外部命令失败:\n {}", err) } }
|
找到 127 个独特的词: { "now.", "1995-2001", "purity.", ... "complex.", "may", }
|
1.3 运行管道传输的外部命令
显示当前工作目录中前 10 大的文件和子目录,它等同于运行: du -ah . | sort -hr | head -n 10
。每个命令 Command
代表一个进程,子进程的输出是通过父进程和子进程之间的管道 Stdio::piped
捕获的。
use error_chain::error_chain;
use std::process::{Command, Stdio};
error_chain! { foreign_links { Io(std::io::Error); Utf8(std::string::FromUtf8Error); } }
fn main() -> 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!( "“{}”中的前 10 个最大文件和目录:\n{}", directory.display(), String::from_utf8(head_stdout.stdout).unwrap() ); } }
Ok(()) }
|
“/root/rustcookbook/ostest”中的前 10 个最大文件和目录: 160M /root/rustcookbook/ostest/target/debug 160M /root/rustcookbook/ostest/target 160M /root/rustcookbook/ostest 124M /root/rustcookbook/ostest/target/debug/deps 18M /root/rustcookbook/ostest/target/debug/build 17M /root/rustcookbook/ostest/target/debug/deps/libregex_syntax-6ee924d67e38f866.rlib 13M /root/rustcookbook/ostest/target/debug/ostest 11M /root/rustcookbook/ostest/target/debug/deps/libregex-62f85974b4cf0dcb.rlib 11M /root/rustcookbook/ostest/target/debug/deps/libaho_corasick-6fc38c3e1c0e1b2d.rlib 9.7M /root/rustcookbook/ostest/target/debug/deps/libbacktrace-75169ab568ebd602.rlib
|
1.4 将子进程的 stdout
和 stderr
重定向到同一个文件
生成子进程并将 stdout
和 stderr
重定向到同一个文件。它遵循与运行管道传输的外部命令相同的思想,但是 process::Stdio
会将输出写入指定的文件。对 stdout
和 stderr
而言,File::try_clone
引用相同的文件句柄。它将确保两个句柄使用相同的光标位置进行写入。下面的实例等同于运行 Unix shell 命令 ls . oops >out.txt 2>&1
(命令用于将标准输出和标准错误都重定向到名为 out.txt
的文件中)。
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(()) }
|
- 运行
cargo run
将创建out.txt
,文件内容如下:
ls: cannot access 'oops': No such file or directory .: Cargo.lock Cargo.toml learn.md out.txt src target
|
1.5 持续处理子进程的输出
在运行外部命令并处理 stdout
实例中,直到外部命令 Command
完成,stdout
的处理才开始。下面的实例调用 Stdio::piped
创建管道,并在 BufReader
被更新后立即读取 stdout
,持续不断地处理。下面的实例等同于 Unix shell 命令 journalctl | grep usb
.
use std::io::{BufRead, BufReader, Error, ErrorKind}; use std::process::{Command, Stdio};
fn main() -> Result<(), Error> { let stdout = Command::new("journalctl") .stdout(Stdio::piped()) .spawn()? .stdout .ok_or_else(|| Error::new(ErrorKind::Other, "无法捕获标准输出。"))?;
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(()) }
|
Feb 25 10:51:07 dynasty sshd[917191]: Invalid user ... Feb 25 10:51:09 dynasty sshd[917191]: Failed password ... Feb 25 10:51:11 dynasty sshd[917191]: Disconnected ... Feb 25 10:55:44 dynasty sshd[917239]: Invalid user ... Feb 25 10:55:45 dynasty sshd[917239]: Failed password ... ...
|
1.6 读取环境变量
通过 std::env::var
读取环境变量。
use std::env; use std::fs; use std::io::Error;
fn main() -> Result<(), Error> { let config_path = env::var("CONFIG").unwrap_or("/etc/myapp/config".to_string());
let config: String = fs::read_to_string(config_path)?; println!("Config: {}", config);
Ok(()) }
|
Config: ls: cannot access 'oops': No such file or directory .: Cargo.lock Cargo.toml learn.md out.txt src target
|