Skip to main content

cmd

Macro cmd 

Source
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:

PrefixReturnsDescription
"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
statusio::Result<ExitStatus>Exit status only (no output)
shio::Result<Output>Parse string, capture output
sh statusio::Result<ExitStatus>Parse string, exit status only
try shResult<String, String>Parse string, stdout or error
bashio::Result<Output>Run via bash -c <command>
pwshio::Result<Output>Run via pwsh -NoProfile -Command ...
tryResult<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 caseRecommended form
Known command, no shell featurescmd!("git rev-parse {branch}")
Known command, with interpolationcmd!("echo {name}")
Simple static commandcmd!("git" "status")
Dynamic command stringcmd!(sh format!("diff {file}"))
Need shell features (pipes, globs)cmd!(bash "echo * | wc -l")
Need PowerShellcmd!(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 implementing AsRef<OsStr> (works with String, &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...}")?;