Struct duct::Expression
[−]
[src]
#[must_use]pub struct Expression(_);
The central objects in duct
, Expressions are created with
cmd
or cmd!
, combined with
pipe
or
then
, and finally executed with
start
,
run
, or
read
. They also support several
methods to control their execution, like
input
,
env
, and
unchecked
.
Expressions are immutable, and they do a lot of
Arc
sharing
internally, so all of the methods below take &self
and return a new
Expression
cheaply.
Expressions using then
and pipe
form trees, and the order in which you
call different methods can matter, just like it matters where you put
redirections in Bash. For example, each of these expressions suppresses
output differently:
// Only suppress output from the left side. let suppress_foo = cmd!("echo", "foo").stdout_null().then(cmd!("echo", "bar")); assert_eq!(suppress_foo.read().unwrap(), "bar"); // Only suppress output from the right side. let suppress_bar = cmd!("echo", "foo").then(cmd!("echo", "bar").stdout_null()); assert_eq!(suppress_bar.read().unwrap(), "foo"); // Suppress output from both sides. let suppress_both = cmd!("echo", "foo").then(cmd!("echo", "bar")).stdout_null(); assert_eq!(suppress_both.read().unwrap(), "");
This version is exactly the same, but with temporary variables to make it easier to see what's going on:
let foo = cmd!("echo", "foo"); let bar = cmd!("echo", "bar"); let foo_null = foo.stdout_null(); let bar_null = bar.stdout_null(); // Note that you can pass expressions by reference, when you're using them // more than once. let suppress_foo = foo_null.then(&bar); assert_eq!(suppress_foo.read().unwrap(), "bar"); let suppress_bar = foo.then(&bar_null); assert_eq!(suppress_bar.read().unwrap(), "foo"); let suppress_both = foo.then(bar).stdout_null(); assert_eq!(suppress_both.read().unwrap(), "");
Methods
impl Expression
[src]
fn run(&self) -> Result<Output>
[src]
Execute an expression, wait for it to complete, and return a
std::process::Output
object containing the results. Nothing is captured by default, but if
you build the expression with
stdout_capture
or
stderr_capture
then
the Output
will hold those captured bytes.
Errors
In addition to all the IO errors possible with
std::process::Command
,
run
will return an
ErrorKind::Other
IO error if child returns a non-zero exit status. To suppress this error
and return an Output
even when the exit status is non-zero, use the
unchecked
method.
Example
let output = cmd!("echo", "hi").stdout_capture().run().unwrap(); assert_eq!(b"hi\n".to_vec(), output.stdout);
fn read(&self) -> Result<String>
[src]
Execute an expression, capture its standard output, and return the
captured output as a String
. This is a convenience wrapper around
run
. Like backticks and $()
in
the shell, read
trims trailing newlines.
Errors
In addition to all the errors possible with
run
, read
will return an
ErrorKind::InvalidData
IO error if the captured bytes aren't valid UTF-8.
Example
let output = cmd!("echo", "hi").stdout_capture().read().unwrap(); assert_eq!("hi", output);
fn start(&self) -> Result<Handle>
[src]
Start running an expression, and immediately return a
Handle
that represents all the child processes.
This is analogous to the
spawn
method in the standard library. The Handle
may be shared between
multiple threads.
Errors
In addition to all the errors prossible with
std::process::Command::spawn
,
start
can return errors from opening pipes and files. However, start
will never return an error if a child process has already started. In
particular, if the left side of a pipe expression starts successfully,
start
will always return Ok
. Any errors that happen on the right
side will be saved and returned later by the wait methods. That makes it
safe for callers to short circuit on start
errors without the risk of
leaking processes.
Example
let handle = cmd!("echo", "hi").stdout_capture().start().unwrap(); let output = handle.wait().unwrap(); assert_eq!(b"hi\n".to_vec(), output.stdout);
fn pipe<T: Into<Expression>>(&self, right: T) -> Expression
[src]
Join two expressions into a pipe expression, where the standard output
of the left will be hooked up to the standard input of the right, like
|
in the shell.
Errors
During execution, if one side of the pipe returns a non-zero exit
status, that becomes the status of the whole pipe, similar to Bash's
pipefail
option. If both sides return non-zero, and one of them is
unchecked
, then the checked
side wins. Otherwise the right side wins.
Example
let output = cmd!("echo", "hi").pipe(cmd!("sed", "s/h/p/")).read(); assert_eq!("pi", output.unwrap());
fn then<T: Into<Expression>>(&self, right: T) -> Expression
[src]
Join two expressions together into an "A then B" expression, like &&
in the shell.
Errors
During execution, if the left child returns a non-zero exit status, the
right child gets skipped. You can use
unchecked
on the left child
to make sure the right child always runs. The exit status of this
expression is the status of the last child that ran. Note that
kill
will prevent the right side
from starting if it hasn't already, even if the left side is
unchecked
.
Example
// Both echoes share the same stdout, so both go through `sed`. let output = cmd!("echo", "-n", "bar") .then(cmd!("echo", "baz")) .pipe(cmd!("sed", "s/b/f/g")).read(); assert_eq!("farfaz", output.unwrap());
fn input<T: Into<Vec<u8>>>(&self, input: T) -> Expression
[src]
Use bytes or a string as input for an expression, like <<<
in the
shell. A worker thread will write the input at runtime.
Example
// Many types implement Into<Vec<u8>>. Here's a string. let output = cmd!("cat").input("foo").read().unwrap(); assert_eq!("foo", output); // And here's a byte slice. let output = cmd!("cat").input(&b"foo"[..]).read().unwrap(); assert_eq!("foo", output);
fn stdin<T: Into<PathBuf>>(&self, path: T) -> Expression
[src]
Open a file at the given path and use it as input for an expression,
like <
in the shell.
Example
// Many types implement Into<PathBuf>, including &str. let output = cmd!("head", "-c", "3").stdin("/dev/zero").read().unwrap(); assert_eq!("\0\0\0", output);
fn stdin_handle<T: IntoRawFd>(&self, handle: T) -> Expression
[src]
Use an already opened file or pipe as input for an expression.
Example
let input_file = std::fs::File::open("/dev/zero").unwrap(); let output = cmd!("head", "-c", "3").stdin_handle(input_file).read().unwrap(); assert_eq!("\0\0\0", output);
fn stdin_null(&self) -> Expression
[src]
Use /dev/null
(or NUL
on Windows) as input for an expression.
Example
let output = cmd!("cat").stdin_null().read().unwrap(); assert_eq!("", output);
fn stdout<T: Into<PathBuf>>(&self, path: T) -> Expression
[src]
Open a file at the given path and use it as output for an expression,
like >
in the shell.
Example
// Many types implement Into<PathBuf>, including &str. let path = cmd!("mktemp").read().unwrap(); cmd!("echo", "wee").stdout(&path).run().unwrap(); let mut output = String::new(); std::fs::File::open(&path).unwrap().read_to_string(&mut output).unwrap(); assert_eq!("wee\n", output);
fn stdout_handle<T: IntoRawFd>(&self, handle: T) -> Expression
[src]
Use an already opened file or pipe as output for an expression.
Example
let path = cmd!("mktemp").read().unwrap(); let file = std::fs::File::create(&path).unwrap(); cmd!("echo", "wee").stdout_handle(file).run().unwrap(); let mut output = String::new(); std::fs::File::open(&path).unwrap().read_to_string(&mut output).unwrap(); assert_eq!("wee\n", output);
fn stdout_null(&self) -> Expression
[src]
Use /dev/null
(or NUL
on Windows) as output for an expression.
Example
// This echo command won't print anything. cmd!("echo", "foo", "bar", "baz").stdout_null().run().unwrap(); // And you won't get anything even if you try to read its output! The // null redirect happens farther down in the expression tree than the // implicit `stdout_capture`, and so it takes precedence. let output = cmd!("echo", "foo", "bar", "baz").stdout_null().read().unwrap(); assert_eq!("", output);
fn stdout_capture(&self) -> Expression
[src]
Capture the standard output of an expression. The captured bytes will be
available on the stdout
field of the
std::process::Output
object returned by run
or
wait
. In the simplest cases,
read
can be more convenient.
Example
// The most direct way to read stdout bytes is `stdout_capture`. let output1 = cmd!("echo", "foo").stdout_capture().run().unwrap().stdout; assert_eq!(&b"foo\n"[..], &output1[..]); // The `read` method is a shorthand for `stdout_capture`, and it also // does string parsing and newline trimming. let output2 = cmd!("echo", "foo").read().unwrap(); assert_eq!("foo", output2)
fn stdout_to_stderr(&self) -> Expression
[src]
Join the standard output of an expression to its standard error pipe,
similar to 1>&2
in the shell.
Example
let output = cmd!("echo", "foo").stdout_to_stderr().stderr_capture().run().unwrap(); assert_eq!(&b"foo\n"[..], &output.stderr[..]);
fn stderr<T: Into<PathBuf>>(&self, path: T) -> Expression
[src]
Open a file at the given path and use it as error output for an
expression, like 2>
in the shell.
Example
// Many types implement Into<PathBuf>, including &str. let path = cmd!("mktemp").read().unwrap(); cmd!("sh", "-c", "echo wee >&2").stderr(&path).run().unwrap(); let mut error_output = String::new(); std::fs::File::open(&path).unwrap().read_to_string(&mut error_output).unwrap(); assert_eq!("wee\n", error_output);
fn stderr_handle<T: IntoRawFd>(&self, handle: T) -> Expression
[src]
Use an already opened file or pipe as error output for an expression.
Example
let path = cmd!("mktemp").read().unwrap(); let file = std::fs::File::create(&path).unwrap(); cmd!("sh", "-c", "echo wee >&2").stderr_handle(file).run().unwrap(); let mut error_output = String::new(); std::fs::File::open(&path).unwrap().read_to_string(&mut error_output).unwrap(); assert_eq!("wee\n", error_output);
fn stderr_null(&self) -> Expression
[src]
Use /dev/null
(or NUL
on Windows) as error output for an expression.
Example
// This echo-to-stderr command won't print anything. cmd!("sh", "-c", "echo foo bar baz >&2").stderr_null().run().unwrap();
fn stderr_capture(&self) -> Expression
[src]
Capture the error output of an expression. The captured bytes will be
available on the stderr
field of the Output
object returned by
run
or
wait
.
Example
let output_obj = cmd!("sh", "-c", "echo foo >&2").stderr_capture().run().unwrap(); assert_eq!(&b"foo\n"[..], &output_obj.stderr[..]);
fn stderr_to_stdout(&self) -> Expression
[src]
Join the standard error of an expression to its standard output pipe,
similar to 2>&1
in the shell.
Example
let error_output = cmd!("sh", "-c", "echo foo >&2").stderr_to_stdout().read().unwrap(); assert_eq!("foo", error_output);
fn dir<T: Into<PathBuf>>(&self, path: T) -> Expression
[src]
Set the working directory where the expression will execute.
Note that in some languages (Rust and Python at least), there are tricky
platform differences in the way relative exe paths interact with child
working directories. In particular, the exe path will be interpreted
relative to the child dir on Unix, but relative to the parent dir on
Windows. duct
considers the Windows behavior correct, so in order to
get that behavior consistently it calls
std::fs::canonicalize
on relative exe paths when dir
is in use. Paths in this sense are any
program name containing a path separator, regardless of the type. (Note
also that Path
and PathBuf
program names get a ./
prepended to
them automatically by the ToExecutable
trait, and so will always contain a separator.)
Errors
Canonicalization can fail on some filesystems, or if the current
directory has been removed, and
run
will return those errors
rather than trying any sneaky workarounds.
Example
let output = cmd!("pwd").dir("/").read().unwrap(); assert_eq!("/", output);
fn env<T, U>(&self, name: T, val: U) -> Expression where
T: Into<OsString>,
U: Into<OsString>,
[src]
T: Into<OsString>,
U: Into<OsString>,
Set a variable in the expression's environment.
Example
let output = cmd!("sh", "-c", "echo $FOO").env("FOO", "bar").read().unwrap(); assert_eq!("bar", output);
fn env_remove<T>(&self, name: T) -> Expression where
T: Into<OsString>,
[src]
T: Into<OsString>,
Remove a variable from the expression's environment.
Note that all the environment functions try to do whatever the platform
does with respect to case sensitivity. That means that
env_remove("foo")
will unset the uppercase variable FOO
on Windows,
but not on Unix.
Example
std::env::set_var("TESTING", "true"); let output = cmd!("sh", "-c", "echo a${TESTING}b") .env_remove("TESTING") .read() .unwrap(); assert_eq!("ab", output);
fn full_env<T, U, V>(&self, name_vals: T) -> Expression where
T: IntoIterator<Item = (U, V)>,
U: Into<OsString>,
V: Into<OsString>,
[src]
T: IntoIterator<Item = (U, V)>,
U: Into<OsString>,
V: Into<OsString>,
Set the expression's entire environment, from a collection of name-value
pairs (like a HashMap
). You can use this method to clear specific
variables for example, by collecting the parent's environment, removing
some names from the collection, and passing the result to full_env
.
Note that some environment variables are required for normal program
execution (like SystemRoot
on Windows), so copying the parent's
environment is usually preferable to starting with an empty one.
Example
let mut env_map: HashMap<_, _> = std::env::vars().collect(); env_map.insert("FOO".into(), "bar".into()); let output = cmd!("sh", "-c", "echo $FOO").full_env(&env_map).read().unwrap(); assert_eq!("bar", output); // The IntoIterator/Into<OsString> bounds are pretty flexible. Passing // by value works here too. let output = cmd!("sh", "-c", "echo $FOO").full_env(env_map).read().unwrap(); assert_eq!("bar", output);
fn unchecked(&self) -> Expression
[src]
Prevent a non-zero exit status from short-circuiting a
then
expression or from causing
run
and friends to return an
error. The unchecked exit code will still be there on the Output
returned by run
; its value doesn't change.
"Uncheckedness" sticks to an exit code as it bubbles up through
complicated expressions, but it doesn't "infect" other exit codes. So
for example, if only one sub-expression in a pipe has unchecked
, then
errors returned by the other side will still be checked. That said,
most commonly you'll just call unchecked
right before run
, and it'll
apply to an entire expression. This sub-expression stuff doesn't usually
come up unless you have a big pipeline built out of lots of different
pieces.
Example
// Normally the `false` command (which does nothing but return a // non-zero exit status) would short-circuit the `then` expression and // also make `read` return an error. `unchecked` prevents this. cmd!("false").unchecked().then(cmd!("echo", "hi")).read().unwrap();
Trait Implementations
impl Clone for Expression
[src]
fn clone(&self) -> Expression
[src]
Returns a copy of the value. Read more
fn clone_from(&mut self, source: &Self)
1.0.0[src]
Performs copy-assignment from source
. Read more
impl Debug for Expression
[src]
impl<'a> From<&'a Expression> for Expression
[src]
fn from(expr: &Expression) -> Expression
[src]
Performs the conversion.