5 releases
0.2.1 | Jul 1, 2024 |
---|---|
0.2.0 |
|
0.1.4 | Jun 13, 2024 |
#239 in Concurrency
46KB
776 lines
Creche
Configure, run, and monitor single child processes or entire pipelines of processes. Redirect file descriptors to and from those processes. Control environment variables.
Creche is an alternative to Command
and friends in the standard library.
Goals
- Minimal dependencies
- No macros, just regular Rust syntax
- Keep the easy stuff easy, without sacrificing configurability
Limitations
- Linux only
- Doesn't support
nostd
- Doesn't handle child process priveleges/capabilities yet
- No async support yet
How to Use
The ChildBuilder
and SimplePipelineBuilder
types are top level
exports, so many common use cases are enabled with a simple use
:
use creche::*;
Read the changelog for a description of breaking changes to the API.
Examples
Read output of cat
into a String
:
// configure the child process
let mut cmd = ChildBuilder::new("cat");
cmd.arg("somefile")
.redirect21(); // redirect stderr to stdout
let read_fd = cmd.pipe_from_stdout();
// run it
let mut child = cmd.spawn();
// read stdout from the child process
let f = std::fs::File::from(read_fd);
let output = std::io::read_to_string(f)?;
println!("output is: {:?}", output);
println!("exit status: {:?}", child.wait());
Write data to a child process:
let mut cmd = creche::ChildBuilder::new("tr");
cmd.arg("[:lower:]")
.arg("[:upper:]");
let write_fd = cmd.pipe_to_stdin();
let child = cmd.spawn();
// write some data
let mut f = std::fs::File::from(write_fd);
writeln!(f, "this is a test message");
// don't forget to close your fd. Or at least .flush() the thing.
drop(f);
println!("child exit: {:?}", child.wait());
Set up fzf to run in an environment with just PATH, HOME, and FZF_DEFAULT_COMMAND. Then run it and get its output:
// configure the environment
let mut env = envconfig::EnvironmentBuilder::new();
env.keep("PATH")
.set("HOME", "/etc")
// Note: $HOME is expanded in the shell spawned by fzf.
// It's not creche magic.
.set("FZF_DEFAULT_COMMAND", "ls -1 $HOME");
// configure the child process
let mut cmd = ChildBuilder::new("fzf");
cmd.arg("-m")
.arg(r#"--preview=bat $HOME/{}"#)
.set_env(env.realize());
let read_fd = cmd.pipe_from_stdout();
// run it
let child = cmd.spawn();
// read the result
let mut f = std::fs::File::from(read_fd);
let output = std::io::read_to_string(f)?;
println!("output is: {:?}", output);
println!("exit status: {:?}", child.wait());
Pipe the output of ls
into sort
into tr
. Redirect stderr for all of that to /dev/null:
let mut ls_cmd = ChildBuilder::new("ls");
ls_cmd.arg("-1");
let mut sort_cmd = ChildBuilder::new("sort");
sort_cmd.arg("-r");
let mut tr_cmd = ChildBuilder::new("tr");
tr_cmd.arg("[:lower:]");
tr_cmd.arg("[:upper:]");
let mut pipeline = SimplePipelineBuilder::new();
let mut children = pipeline
.add_builder(ls_cmd)
.add_builder(sort_cmd)
.add_builder(tr_cmd)
.quiet()
.spawn();
println!("{:?}", children.wait());
Block on .wait()
ing a child process and send a SIGTERM from another thread:
// sleep for a few seconds
let mut cmd = ChildBuilder::new("sleep");
cmd.arg("6");
println!("sleeping for six seconds");
let child = cmd.spawn();
// get a "handle" to the child process so that we may signal it
let handle = child.get_handle();
// start a thread that sends SIGTERM after a short pause
std::thread::spawn( move || {
std::thread::sleep(std::time::Duration::from_secs(2));
println!("sending SIGTERM from thread");
_ = handle.terminate();
});
// collect the child exit status
println!("child exit: {:?}", child.wait());
Dependencies
~1.5MB
~35K SLoC