Skip to main content

cmd

Macro cmd 

Source
macro_rules! cmd {
    (@ $cmd:literal $(,)?) => { ... };
    (@ $first:literal => $($rest:tt)+) => { ... };
    ($($first:expr),+ => $($rest:tt)+) => { ... };
    ($($args:expr),+ $(,)?) => { ... };
    () => { ... };
    (%flow $flow:expr => @ $next:literal => $($rest:tt)+) => { ... };
    (%flow $flow:expr => @ $next:literal $(,)?) => { ... };
    (%flow $flow:expr => $($next:expr),+ => $($rest:tt)+) => { ... };
    (%flow $flow:expr => $($next:expr),+ $(,)?) => { ... };
    (%cmd @ $cmd:literal) => { ... };
    (%cmd $prog:expr $(, $arg:expr)* $(,)?) => { ... };
}
Available on crate feature std only.
Expand description

🖥️ Builds a CommandFlow from one or more command invocations.


📍 work/process


Grammar (informal):

  • The => operator constructs a linear flow, connecting each command’s stdout to the stdin of the next.
  • Each direct command segment consists of:
    • The first expression as the program.
    • Any remaining expressions as arguments to that program.
  • Prefixing a string literal with @ splits it into a program and arguments using shell word syntax.
  • A single command segment forms a command flow of length 1.

Semantics:

  • This macro does not invoke a shell.
  • Commands are spawned directly through the OS process API.
  • Direct segments pass their expressions unchanged as argv words.
  • @ segments only perform shell-style word splitting and quoting.
  • No variable expansion, globbing, redirection, command substitution, or shell operators are performed.

The @ syntax requires the shell feature.

§Examples

let arg1 = "-F";
let cmd2 = "grep";

// A single direct command.
cmd!("ls").run();
cmd!("ls", arg1, ".").run();
cmd!("ls", "-F", ".").run();

// A literal split into a program and arguments.
#[cfg(feature = "shell")]
cmd!(@ "ls -F .").run();

// Quoting controls argument boundaries.
#[cfg(feature = "shell")]
cmd!(@ r#"echo "hello world""#).run();

// Shell expansion is not performed.
#[cfg(feature = "shell")]
cmd!(@ r#"echo "$HOME" "*.rs""#).run(); // literal "$HOME" and "*.rs"

// Multiple piped commands: `ps aux | grep lib | wc -l`.
cmd!("ps", "aux" => cmd2, "lib" => "wc", "-l").run();

#[cfg(feature = "shell")]
cmd!(@ "ps aux" => @ "grep lib" => @ "wc -l").run();

// Direct and split segments may be combined.
#[cfg(feature = "shell")]
cmd!("printf", "hello world" => @ r#"grep "hello world""# => "wc", "-l").run();

§No implicit splitting

Without @, a string is treated as one argv word:

cmd!("ls -F").run();      // executes a program named `ls -F`
cmd!("ls -F", ".").run(); // executes `ls -F` with `.` as its argument

Use cmd!(@ "ls -F") when shell-word splitting is intended.