External Command
Run an external command and process stdout
Runs git log --oneline using an external Command and inspects the Output
status to determine if the command was successful. The command output is captured
as a [String] using [String::from_utf8].
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")); } }
Run an external command passing it stdin and check for an error code
Opens the python interpreter using an external Command and passes it a
python statement for execution. Output of statement is then parsed.
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)); } }
Run piped external commands
Shows up to the 10th biggest files and subdirectories in
the current working directory. It is equivalent to running: du -ah . | sort -hr | head -n 10.
Commands represent a process. Output of a child process is captured with a
Stdio::piped between parent and child.
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(()) }
Redirect both stdout and stderr of child process to the same file
Spawns a child process and redirects stdout and stderr to the same
file. It follows the same idea as run piped external
commands, however process::Stdio
writes to a specified file. File::try_clone references the same file handle
for stdout and stderr. It will ensure that both handles write with the same
cursor position.
The below recipe is equivalent to run the Unix shell command 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(()) }
Continuously process child process' outputs
In Run an external command and process stdout,
processing doesn't start until external Command is finished.
The recipe below calls Stdio::piped to create a pipe, and reads
stdout continuously as soon as the BufReader is updated.
The below recipe is equivalent to the Unix shell command
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(()) }
Read Environment Variable
Reads an environment variable via std::env::var.
use std::env; use std::fs; use std::io::Error; fn main() -> Result<(), Error> { // read `config_path` from the environment variable `CONFIG`. // If `CONFIG` isn't set, fall back to a default config path. 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(()) }