macro_rules! cmd {
(sh status $cmd:literal; dir: $dir:expr) => { ... };
(sh status $cmd:expr; dir: $dir:expr) => { ... };
(sh status $cmd:literal) => { ... };
(sh status $cmd:expr) => { ... };
(sh $cmd:literal; dir: $dir:expr) => { ... };
(sh $cmd:expr; dir: $dir:expr) => { ... };
(sh $cmd:literal) => { ... };
(sh $cmd:expr) => { ... };
(status bash $cmd:expr; dir: $dir:expr) => { ... };
(status bash $cmd:expr) => { ... };
(bash status $cmd:expr; dir: $dir:expr) => { ... };
(bash status $cmd:expr) => { ... };
(bash $cmd:expr; dir: $dir:expr) => { ... };
(bash $cmd:expr) => { ... };
(status pwsh $cmd:expr; dir: $dir:expr) => { ... };
(status pwsh $cmd:expr) => { ... };
(pwsh status $cmd:expr; dir: $dir:expr) => { ... };
(pwsh status $cmd:expr) => { ... };
(pwsh $cmd:expr; dir: $dir:expr) => { ... };
(pwsh $cmd:expr) => { ... };
(try sh $cmd:literal; dir: $dir:expr) => { ... };
(try sh $cmd:expr; dir: $dir:expr) => { ... };
(try sh $cmd:literal) => { ... };
(try sh $cmd:expr) => { ... };
(try bash $cmd:expr; dir: $dir:expr) => { ... };
(try bash $cmd:expr) => { ... };
(try pwsh $cmd:expr; dir: $dir:expr) => { ... };
(try pwsh $cmd:expr) => { ... };
(status $cmd:literal; dir: $dir:expr) => { ... };
(status $cmd:literal) => { ... };
(try $cmd:literal; dir: $dir:expr) => { ... };
(try $cmd:literal) => { ... };
($cmd:literal; dir: $dir:expr) => { ... };
($cmd:literal) => { ... };
(status $binary:literal $($arg:literal)*; dir: $dir:expr) => { ... };
(status $binary:literal $($arg:literal)*) => { ... };
(status $binary:expr, [ $($arg:expr),* $(,)? ]; dir: $dir:expr) => { ... };
(status $binary:expr, [ $($arg:expr),* $(,)? ]) => { ... };
(status $binary:expr, $args:expr; dir: $dir:expr) => { ... };
(status $binary:expr, $args:expr) => { ... };
(try $binary:literal $($arg:literal)*; dir: $dir:expr) => { ... };
(try $binary:literal $($arg:literal)*) => { ... };
(try $binary:expr, [ $($arg:expr),* $(,)? ]; dir: $dir:expr) => { ... };
(try $binary:expr, [ $($arg:expr),* $(,)? ]) => { ... };
(try $binary:expr, $args:expr; dir: $dir:expr) => { ... };
(try $binary:expr, $args:expr) => { ... };
($binary:literal $($arg:literal)*; dir: $dir:expr) => { ... };
($binary:literal $($arg:literal)*) => { ... };
($binary:expr, [ $($arg:expr),* $(,)? ]; dir: $dir:expr) => { ... };
($binary:expr, [ $($arg:expr),* $(,)? ]) => { ... };
($binary:expr, $args:expr; dir: $dir:expr) => { ... };
($binary:expr, $args:expr) => { ... };
}Expand description
Execute a command and capture its output.
Simplifies Command::new(binary).args(args).output() patterns.
Thread safe — Command is Send + Sync.
Supports several calling conventions:
| Prefix | Returns | Description |
|---|---|---|
"literal" | io::Result<Output> | Interpolated literal, capture output |
status "lit" | io::Result<ExitStatus> | Interpolated literal, exit status only |
try "lit" | Result<String, String> | Interpolated literal, stdout or error |
| (none) | io::Result<Output> | Run and capture output |
status | io::Result<ExitStatus> | Exit status only (no output) |
sh | io::Result<Output> | Parse string, capture output |
sh status | io::Result<ExitStatus> | Parse string, exit status only |
try sh | Result<String, String> | Parse string, stdout or error |
bash | io::Result<Output> | Run via bash -c <command> |
pwsh | io::Result<Output> | Run via pwsh -NoProfile -Command ... |
try | Result<String, String> | Run, return stdout or error message |
All forms support an optional ; dir: path suffix to set the working directory.
§When to use each form
| Use case | Recommended form |
|---|---|
| Known command, no shell features | cmd!("git rev-parse {branch}") |
| Known command, with interpolation | cmd!("echo {name}") |
| Simple static command | cmd!("git" "status") |
| Dynamic command string | cmd!(sh format!("diff {file}")) |
| Need shell features (pipes, globs) | cmd!(bash "echo * | wc -l") |
| Need PowerShell | cmd!(pwsh "Get-ChildItem | Select-Object Name") |
On the surface cmd!("...") and cmd!(sh "...") both accept a literal string, but
the bare literal form is the preferred entry point — it’s the simplest, avoids the
unnecessary sh prefix, and supports the full {var} / {args...} interpolation.
The sh prefix is useful when the command string is a runtime expression (e.g.
format!(...)), since cmd!(sh $expr) parses it with shell-aware word splitting.
§Literal interpolation
A literal string (a bare "..." token without sh) is parsed at compile time
and supports both interpolation forms:
{name}— expands a single value implementingAsRef<OsStr>(works withString,&str,OsString,PathBuf, etc.) into one command argument{name...}— expands an iterable (splat) into zero or more command arguments
Both forms must occupy a full unquoted shell word. Embedded or double-quoted
interpolation like --flag={name} and "{name}" is rejected at compile time.
Single quotes keep the placeholder literal:
cmd!("echo '{name}'") // prints {name}
cmd!("echo '{args...}'") // prints {args...}§Single-value {var} interpolation
let msg = "hello world";
let output = cmd!("echo {msg}")?;
assert_eq!(output.stdout(), "hello world");
let branch = "main".to_string();
let output = cmd!("git rev-parse {branch}")?;{var} accepts any type implementing AsRef<OsStr> — this includes str,
String, OsStr, OsString, Path, and PathBuf. For types like integers
that don’t implement AsRef<OsStr>, convert first:
let port = 8080;
cmd!("curl http://localhost:{port}"); // compile error
cmd!("curl http://localhost:{}", port.to_string()); // use .to_string()
let p = format!("localhost:{}", port);
cmd!("curl http://{p}"); // works via String§Splat {args...} interpolation
Splat placeholders expand a local variable that implements borrowed
iteration (arrays, Vec, Option, slices, etc.):
let args = ["hello", "world"];
let output = cmd!("echo {args...}")?;
assert_eq!(output.stdout(), "hello world");
let arg1: Option<&str> = Some("hello");
let arg2: Option<&str> = None;
let output = cmd!("echo {arg1...} {arg2...}")?;
assert_eq!(output.stdout(), "hello");§In sh literals
The same {var} and {args...} interpolation also works in literal sh
command strings:
cmd!(sh "echo {msg}")
cmd!(sh "echo {args...}")
cmd!(sh status "echo {msg}")
cmd!(try sh "echo {args...}")
cmd!(sh "echo {msg}"; dir: project_dir)Runtime sh strings (non-literal $cmd:expr) are split via shell-words
at runtime and do not support interpolation — use format! or build an
argument list explicitly.
§Syntax
// Interpolated literal (compile-time parsed, fastest)
cmd!("git rev-parse {branch}")
cmd!("echo {args...}")
cmd!(status "git status")
cmd!(try "git rev-parse HEAD")
cmd!("echo {msg}"; dir: project_dir)
// String form via sh (shell-aware quoting)
cmd!(sh "git diff --name-only")
cmd!(sh format!("git diff --name-only {branch}"))
cmd!(sh "echo 'hello world'") // handles quotes
cmd!(sh "echo {args...}") // literal-only interpolation
cmd!(sh status "echo {args...}")
cmd!(try sh "echo {args...}")
cmd!(bash "echo 'hello world'")
cmd!(pwsh "Write-Output 'hello world'")
// CLI-style literals
cmd!("git" "diff-tree" "--no-commit-id" "--name-only")
cmd!("git" "log" "--oneline"; dir: repo_path)
// Array literal (dynamic types)
cmd!("git", ["branch", "--show-current"])
// Variable args
cmd!("git", args)
// Try form — returns Result<String, String>
cmd!(try "git" "rev-parse" "HEAD")
cmd!(try "git", args)
// With working directory
cmd!("git" "status"; dir: project_dir)
cmd!(try sh "npm test"; dir: project_dir)
cmd!(status bash "echo hello"; dir: project_dir)
cmd!(try pwsh "Write-Output 'hello'"; dir: project_dir)§Examples
use acorn::cmd;
use acorn::prelude::CommandOutput;
// Interpolated literal (no shell overhead)
let branch = "main";
let output = cmd!("git rev-parse {branch}")?;
// String form with shell-aware quoting
match cmd!(sh format!("git diff --name-only {branch}")) {
Ok(output) if output.status.success() => {
println!("{}", output.stdout());
}
_ => {},
}
// CLI-style
match cmd!("git" "branch" "--show-current") {
Ok(output) if output.status.success() => {
println!("{}", output.stdout());
}
Ok(output) => eprintln!("{}", output.stderr()),
Err(why) => eprintln!("Error: {}", why),
}
// Try form — simplified error handling
match cmd!(try "git" "rev-parse" "HEAD") {
Ok(hash) => println!("{hash}"),
Err(msg) => eprintln!("failed: {msg}"),
}
// Explicit shell selection
let output = cmd!(bash "echo bash-mode")?;
let output = cmd!(pwsh "Write-Output 'pwsh-mode'")?;
// Splat interpolation
let args = ["hello", "world"];
let output = cmd!("echo {args...}")?;
assert_eq!(output.stdout(), "hello world");
let dry_run = Some("--dry-run");
let extra: Option<&str> = None;
let output = cmd!(try "cargo publish {dry_run...} {extra...}")?;