pub struct Cmd { /* private fields */ }Expand description
Builder for a subprocess invocation or pipeline.
Construct via Cmd::new, configure with builder methods, chain with
Cmd::pipe (or |), terminate with Cmd::run or Cmd::spawn.
Per-stage builders — arg, args,
in_dir, env, envs,
env_clear, env_remove — target
the rightmost stage. Pipeline-level builders — stdin,
stderr, timeout,
deadline, retry,
retry_when, secret,
before_spawn — apply to the whole pipeline.
§Cloning
Cmd: Clone so you can build a base configuration and branch off
variants. Most state is cheap to clone (owned data or Arc). One
caveat: if stdin was set with StdinData::from_reader,
the reader is shared across clones — whichever attempt runs first takes
it, and later attempts (or other clones) see no stdin. For bytes-based
stdin, every clone and every retry re-feeds the same buffer.
Implementations§
Source§impl Cmd
impl Cmd
Sourcepub fn pipe(self, next: Cmd) -> Cmd
pub fn pipe(self, next: Cmd) -> Cmd
Pipe this command’s stdout into next’s stdin.
Per-stage configuration (args, env, cwd) is preserved for each side.
Pipeline-level configuration (stdin, stdout, stderr,
timeout, deadline, retry, secret, before_spawn) is taken
from self — any such settings on next are silently dropped.
§Gotcha
// WRONG: the inner `.timeout(...)` is discarded.
let _ = Cmd::new("a").pipe(Cmd::new("b").timeout(Duration::from_secs(5)));
// RIGHT: pipeline-level settings go on the outer `Cmd`.
let _ = Cmd::new("a").pipe(Cmd::new("b")).timeout(Duration::from_secs(5));Sourcepub fn arg(self, arg: impl Into<OsString>) -> Cmd
pub fn arg(self, arg: impl Into<OsString>) -> Cmd
Append a single argument to the rightmost stage.
Sourcepub fn in_dir(self, dir: impl AsRef<Path>) -> Cmd
pub fn in_dir(self, dir: impl AsRef<Path>) -> Cmd
Set the working directory of the rightmost stage.
Sourcepub fn env(self, key: impl Into<OsString>, value: impl Into<OsString>) -> Cmd
pub fn env(self, key: impl Into<OsString>, value: impl Into<OsString>) -> Cmd
Add one environment variable to the rightmost stage.
Sourcepub fn envs<I, K, V>(self, vars: I) -> Cmd
pub fn envs<I, K, V>(self, vars: I) -> Cmd
Add multiple environment variables to the rightmost stage.
Sourcepub fn env_remove(self, key: impl Into<OsString>) -> Cmd
pub fn env_remove(self, key: impl Into<OsString>) -> Cmd
Remove an environment variable from the rightmost stage.
Sourcepub fn stdin(self, data: impl Into<StdinData>) -> Cmd
pub fn stdin(self, data: impl Into<StdinData>) -> Cmd
Feed data into the leftmost stage’s stdin.
Sourcepub fn stderr(self, mode: Redirection) -> Cmd
pub fn stderr(self, mode: Redirection) -> Cmd
Configure stderr routing for every stage. Default is
Redirection::Capture.
Sourcepub fn stdout(self, mode: Redirection) -> Cmd
pub fn stdout(self, mode: Redirection) -> Cmd
Configure stdout routing for the last stage (for single commands, the only stage).
Default is Redirection::Capture, which populates
RunOutput::stdout on run. Pick
Redirection::Inherit to stream to the parent’s stdout,
Redirection::Null to discard, or Redirection::File /
Redirection::file to redirect to a file.
Setting non-Capture stdout is only supported on run.
spawn / spawn_async always
pipe stdout so the handle can expose it via
take_stdout / the Read
impl; a non-Capture stdout on the spawn path surfaces a
RunError::Spawn with
ErrorKind::InvalidInput.
Sourcepub fn stderr_file(self, f: File) -> Cmd
pub fn stderr_file(self, f: File) -> Cmd
Convenience: redirect stderr to a file without wrapping the
File in Arc yourself. Shorthand for
self.stderr(Redirection::file(f)).
Sourcepub fn stdout_file(self, f: File) -> Cmd
pub fn stdout_file(self, f: File) -> Cmd
Convenience: redirect stdout (of the last stage) to a file.
Shorthand for self.stdout(Redirection::file(f)).
Sourcepub fn deadline(self, deadline: Instant) -> Cmd
pub fn deadline(self, deadline: Instant) -> Cmd
Kill if not done by this instant (composes across retries).
Sourcepub fn retry(self, policy: RetryPolicy) -> Cmd
pub fn retry(self, policy: RetryPolicy) -> Cmd
Attach a RetryPolicy. Defaults retry up to 3× on transient errors.
Sourcepub fn retry_when(
self,
f: impl Fn(&RunError) -> bool + Send + Sync + 'static,
) -> Cmd
pub fn retry_when( self, f: impl Fn(&RunError) -> bool + Send + Sync + 'static, ) -> Cmd
Replace the retry predicate without changing the backoff schedule.
Sourcepub fn secret(self) -> Cmd
pub fn secret(self) -> Cmd
Mark the pipeline as containing secrets; CmdDisplay will render
args as <secret>.
Sourcepub fn before_spawn<F>(self, hook: F) -> Cmd
pub fn before_spawn<F>(self, hook: F) -> Cmd
Register a hook called immediately before each spawn attempt.
The hook receives a &mut std::process::Command and may mutate it
arbitrarily — set Unix-specific options via CommandExt (umask,
pre_exec, process_group), attach IPC sockets, toggle environment
late, etc. Returning an error aborts that spawn attempt and
surfaces as RunError::Spawn.
§Scope
- Sync path (
run,spawn): hook fires per stage per retry attempt. - Async path (
run_async,spawn_async): hook fires per stage per retry attempt. Tokio’sCommandexposes its underlyingstd::process::Commandviaas_std_mut, so the same hook works on both paths — no parallel async hook type.
§Tokio-specific settings
Note that modifications made through as_std_mut do not include
tokio’s own knobs (e.g., kill_on_drop). Those are set by
procpilot internally and are not exposed to before_spawn.
Sourcepub fn to_rightmost_command(&self) -> Command
pub fn to_rightmost_command(&self) -> Command
Build a raw std::process::Command mirroring the rightmost stage.
For a single command this is the one you configured. For a
pipeline, the upstream stages are discarded — the returned
Command is only the rightmost one, without stdio wiring. Use
to_commands to recover all stages.
The explicit name is a signpost: on a pipeline, this method silently drops information, so use it deliberately.
Sourcepub fn to_commands(&self) -> Vec<Command>
pub fn to_commands(&self) -> Vec<Command>
Build one raw std::process::Command per stage, leftmost first.
Stdio wiring between stages is not set up — callers are
responsible for piping the returned Commands together if they need
the full shell-style behavior. For the typical case where you just
want to execute the pipeline, use run or
spawn.
Sourcepub fn display(&self) -> CmdDisplay
pub fn display(&self) -> CmdDisplay
Snapshot the command (or pipeline) for display/logging.
Sourcepub fn spawn(self) -> Result<SpawnedProcess, RunError>
pub fn spawn(self) -> Result<SpawnedProcess, RunError>
Spawn the command (or pipeline) as a long-lived process handle.
Returns a SpawnedProcess for streaming, bidirectional protocols,
or any case where you need live access to stdin/stdout. Stdin and
stdout are always piped; stderr follows the configured
Redirection (default Redirection::Capture, drained into a
background thread and surfaced on SpawnedProcess::wait).
For pipelines, SpawnedProcess::take_stdin targets the leftmost
stage, SpawnedProcess::take_stdout the rightmost, and lifecycle
methods operate on every stage.
If stdin bytes were set via stdin, they’re fed
automatically in a background thread; otherwise the caller can pipe
data via SpawnedProcess::take_stdin.
timeout, deadline, and retry are ignored on this path —
they only apply to the one-shot run method. Use
SpawnedProcess::wait_timeout or SpawnedProcess::kill for
per-call bounds.
Sourcepub fn spawn_and_collect_lines<F>(self, f: F) -> Result<RunOutput, RunError>
pub fn spawn_and_collect_lines<F>(self, f: F) -> Result<RunOutput, RunError>
Spawn and invoke f for each line of stdout as it arrives.
Returns the final RunOutput when the child exits, or a
RunError::NonZeroExit if it exited non-zero. If f returns an
error, the child is killed and the error is surfaced as
RunError::Spawn.
Cmd::new("cargo")
.args(["check", "--message-format=json"])
.spawn_and_collect_lines(|line| {
println!("{line}");
Ok(())
})?;Sourcepub fn run(self) -> Result<RunOutput, RunError>
pub fn run(self) -> Result<RunOutput, RunError>
Run the command (or pipeline) synchronously, blocking until it completes (or its timeout / deadline fires).
Returns RunOutput on exit status 0. Non-zero exits become
RunError::NonZeroExit carrying the last 128 KiB of
stdout/stderr. Spawn failures become RunError::Spawn; timeouts
become RunError::Timeout.
For an async variant usable from inside a tokio runtime, enable
the tokio feature and use run_async. For
long-lived or streaming processes, see spawn.
§Examples
Basic capture:
let out = Cmd::new("git").args(["rev-parse", "HEAD"]).in_dir("/repo").run()?;
println!("{}", out.stdout_lossy().trim());Typed error branching:
let maybe = match Cmd::new("git").args(["show", "possibly-missing"]).run() {
Ok(out) => Some(out.stdout),
Err(RunError::NonZeroExit { .. }) => None, // ref not found — expected
Err(e) => return Err(e.into()), // real failure
};Pipeline with timeout and retry:
(Cmd::new("git").args(["log", "--oneline"]) | Cmd::new("head").arg("-5"))
.timeout(Duration::from_secs(10))
.retry(RetryPolicy::default())
.run()?;