外部コマンド
外部コマンドを実行して stdout を処理する
外部の Command を使って git log --oneline を実行し、Output の
status を調べてコマンドが成功したかどうかを判定します。コマンドの出力は
[String::from_utf8] を使って [String] として取得します。
use anyhow::{Result, anyhow};
use std::process::Command;
fn main() -> Result<()> {
let output = Command::new("git").arg("log").arg("--oneline").output()?;
if output.status.success() {
let raw_output = String::from_utf8(output.stdout)?;
let lines = raw_output.lines();
println!("Found {} lines", lines.count());
Ok(())
} else {
return Err(anyhow!("Command executed with failing error code"));
}
}
stdin を渡して外部コマンドを実行し、エラーコードを確認する
外部の Command を使って python インタープリタを起動し、実行するための
Python 文を渡します。その文の Output をその後で解析します。
use anyhow::{Result, anyhow};
use std::collections::HashSet;
use std::io::Write;
use std::process::{Command, Stdio};
fn main() -> Result<()> {
let mut child = Command::new("python").stdin(Stdio::piped())
.stderr(Stdio::piped())
.stdout(Stdio::piped())
.spawn()?;
child.stdin
.as_mut()
.ok_or_else(|| anyhow!("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)?;
return Err(anyhow!("External command failed:\n {}", err));
}
}
パイプでつないだ外部コマンドを実行する
現在の作業ディレクトリ内で、サイズが大きい上位 10 個のファイルとサブディレクトリを表示します。これは次を実行するのと同等です: du -ah . | sort -hr | head -n 10。
Command はプロセスを表します。子プロセスの出力は、親プロセスと子プロセスの間の
Stdio::piped でキャプチャされます。
use anyhow::Result;
use std::process::{Command, Stdio};
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!(
"Top 10 biggest files and directories in '{}':\n{}",
directory.display(),
String::from_utf8(head_stdout.stdout).unwrap()
);
}
}
Ok(())
}
子プロセスの stdout と stderr の両方を同じファイルにリダイレクトする
子プロセスを生成し、stdout と stderr を同じファイルにリダイレクトします。パイプで接続した外部コマンドを実行する と同じ考え方ですが、process::Stdio は指定したファイルに書き込みます。File::try_clone は stdout と stderr に対して同じファイルハンドルを参照します。これにより、両方のハンドルが同じカーソル位置で書き込むことが保証されます。
以下のレシピは、Unix シェルコマンド 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 を処理する では、
外部 Command が終了するまで処理は開始されません。
以下のレシピでは Stdio::piped を呼び出してパイプを作成し、
BufReader が更新されるたびに stdout を継続的に読み取ります。
以下のレシピは、Unix シェルコマンド
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(())
}
環境変数を読み取る
std::env::var を介して環境変数を読み取ります。
use std::env;
use std::fs;
use std::io::Error;
fn main() -> Result<(), Error> {
// 環境変数 `CONFIG` から `config_path` を読み取る。
// `CONFIG` が設定されていない場合は、デフォルトの設定ファイルパスにフォールバックする。
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(())
}