- Notifications
You must be signed in to change notification settings - Fork44
Execution of and interaction with external processes and pipelines
License
Apache-2.0, MIT licenses found
Licenses found
hniksic/rust-subprocess
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Thesubprocess
library provides facilities for execution of andinteraction with external processes and pipelines, inspired byPython'ssubprocess
module.subprocess
ishosted oncrates.io, withAPIDocumentation on docs.rs.
This library is about launching external processes with optional redirectionof standard input, output, and error. It covers similar ground as thestd::process
standardlibrary module, but with additional functionality:
Thecommunicatefamily ofmethodsfor deadlock-free capturing of subprocess output/error to memory, whilesimultaneously feeding data to its standard input. Capturing supportsoptional timeout and read size limit.
Connecting multiple commands into OS-level pipelines.
Flexible redirection options, such as connecting standard streams toarbitrary files, or merging output streams like shell's
2>&1
and1>&2
operators.Non-blocking and timeout methods to wait on the process:
poll
,wait
, andwait_timeout
.
The crate has minimal dependencies to third-party crates, only requiringlibc
on Unix andwinapi
on Windows. It is intended to work on Unix-likeplatforms as well as on reasonably recent Windows. It is regularly tested onLinux, MacOS and Windows.
The API is separated in two parts: the low-levelPopen
API similar toPython'ssubprocess.Popen
,and the higher-level API for convenient creation of commands and pipelines.The two can be mixed, so it is possible to use builder to createPopen
instances, and then to continue working with them directly.
WhilePopen
loosely follows Python'ssubprocess
module,it is not a literal translation. Some of the changes accommodate thedifferences between the languages, such as the lack of default and keywordarguments in Rust, and others take advantage of Rust's more advanced typesystem, or of additional capabilities such as the ownership system and theDrop
trait. Python's utility functions such assubprocess.run
are notincluded because they can be better expressed using the builder pattern.
The high-level API offers an elegant process and pipeline creation interface,along with convenience methods for capturing their output and exit status.
Execute an command and wait for it to complete:
let exit_status =Exec::cmd("umount").arg(dirname).join()?;assert!(exit_status.success());
To prevent quoting issues and injection attacks, subprocess will notspawn a shell unless explicitly requested. To execute a command usingthe OS shell, like C'ssystem
, useExec::shell
:
Exec::shell("shutdown -h now").join()?;
Start a subprocess and obtain its output as aRead
trait object,like C'spopen
:
let stream =Exec::cmd("find /").stream_stdout()?;// Call stream.read_to_string, construct io::BufReader(stream) and iterate it// by lines, etc...
Capture the output of a command:
let out =Exec::cmd("ls").stdout(Redirection::Pipe).capture()?.stdout_str();
Redirect standard error to standard output, and capture them in a string:
let out_and_err =Exec::cmd("ls").stdout(Redirection::Pipe).stderr(Redirection::Merge).capture()?.stdout_str();
Provide some input to the command and read its output:
let out =Exec::cmd("sort").stdin("b\nc\na\n").stdout(Redirection::Pipe).capture()?.stdout_str();assert_eq!(out,"a\nb\nc\n");
Connectingstdin
to an open file would have worked as well.
Popen
objects support connecting input and output to arbitrary openfiles, including otherPopen
objects. This can be used to formpipelines of processes. The builder API will do it automatically withthe|
operator onExec
objects.
Execute a pipeline and return the exit status of the last command:
let exit_status =(Exec::shell("ls *.bak") |Exec::cmd("xargs").arg("rm")).join()?;
Capture the pipeline's output:
let dir_checksum ={Exec::shell("find . -type f") |Exec::cmd("sort") |Exec::cmd("sha1sum")}.capture()?.stdout_str();
letmut p =Popen::create(&["command","arg1","arg2"],PopenConfig{stdout:Redirection::Pipe, ..Default::default()})?;// Since we requested stdout to be redirected to a pipe, the parent's// end of the pipe is available as p.stdout. It can either be read// directly, or processed using the communicate() method:let(out, err) = p.communicate(None)?;// check if the process is still aliveifletSome(exit_status) = p.poll(){// the process has finished}else{// it is still running, terminate it p.terminate()?;}
Check whether a previously launched process is still running:
letmut p =Exec::cmd("sleep").arg("2").popen()?;thread::sleep(Duration::new(1,0));if p.poll().is_none(){// poll() returns Some(exit_status) if the process has completedprintln!("process is still running");}
Give the process 1 second to run, and kill it if it didn't complete bythen.
letmut p =Exec::cmd("sleep").arg("2").popen()?;ifletSome(status) = p.wait_timeout(Duration::new(1,0))?{println!("process finished as {:?}", status);}else{ p.kill()?; p.wait()?;println!("process killed");}
subprocess
is distributed under the terms of both the MIT licenseand the Apache License (Version 2.0). SeeLICENSE-APACHE andLICENSE-MIT fordetails. Contributing changes is assumed to signal agreement withthese licensing terms.
About
Execution of and interaction with external processes and pipelines