cmd_lib

Rust macros for writing shell-script-like tasks without spawning a shell

Library
Cargo
v2.0.0
1,159stars
MIT OR Apache-2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
53/100Fair
Development Activity36
Maintenance32
Community44
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
75/100Good
Architecture76
Code Quality74
Innovation80
Learning Curve68

cmd_lib provides run_cmd! and run_fun! macros that let Rust programs run external commands with shell-like piping and redirection syntax, but with compile-time parsing and no shell process ever spawned. It targets the common case of porting ad-hoc shell scripts into maintainable Rust code without losing the ergonomic cmd | cmd2 > file syntax developers already know.

Because command parsing happens at compile time via procedural macros, most command-composition errors surface as compiler errors rather than runtime failures, and parameter substitution is done safely by construction, avoiding the command-injection pitfalls of naive shell string interpolation. The library also ships builtin commands (cd, echo, logging levels), thread-local globals, and lower-level spawn!/spawn_with_output! macros for background process control.

What You Get

  • run_cmd! and run_fun! macros for executing commands and capturing their output as Result-wrapped values
  • Native pipe (|) and redirection syntax resolved entirely at compile time, no shell subprocess involved
  • Safe, injection-resistant variable interpolation ($var, ${var}, $[vec]) into command arguments
  • Builtin commands (cd, echo, ignore, error/warn/info/debug/trace) plus integration with the log crate
  • Lower-level spawn!/spawn_with_output! macros and thread-local global macros (tls_init!/tls_get!/tls_set!) for background process and state management

Common Use Cases

  • Rewriting a fragile bash/shell deployment or CI script as a typed, compiled Rust binary
  • Composing external command pipelines (grep/awk/sort-style chains) inside a Rust CLI tool
  • Running system commands with dynamic, user-influenced arguments while avoiding shell injection
  • Building small ops/automation tools that need process spawning, logging, and directory-scoped cd semantics

Under The Hood

Architecture: The crate splits into a macros workspace member (the cmd_lib_macros proc-macro crate performing compile-time lexical/syntactic parsing of the DSL) and the runtime crate itself, whose src/process.rs (843 lines) implements process spawning, piping, and I/O plumbing, src/builtins.rs implements the built-in commands (cd, echo, logging), src/io.rs handles redirection, and src/thread_local.rs implements the tls_*! macros. src/lib.rs is primarily macro-facing documentation and re-exports, keeping the public surface small relative to the implementation.

Tech Stack: Rust 2024 edition, MSRV 1.88, depending on os_pipe for cross-platform pipe creation, faccess for permission checks, log/env_logger for its logging integration, and optional tracing/build-print features for alternative logging backends. Dev-dependencies (rayon, clap, tracing-subscriber) support its examples and test suite rather than the library itself.

Code Quality: tests/test_macros.rs (380 lines) exercises the macro-generated code across piping, redirection, builtins, and error paths, and the crate favors returning Result types (CmdResult, FunResult) over panics for command failures, which is consistent with the library’s stated goal of surfacing errors explicitly rather than silently. Dual MIT/Apache-2.0 licensing and a rustfmt.toml indicate standard Rust ecosystem conventions are followed.

API Design: The macro-based DSL is the library’s central design bet — run_cmd!(du -ah $dir | sort -hr | head -n 10) reads almost identically to the equivalent bash, which minimizes the porting cost from shell scripts while gaining Rust’s type system and compile-time checks. This trades some discoverability (IDE tooling sees macro invocations, not plain function calls) for very low boilerplate at the call site, which the maintainer explicitly frames as the core value proposition over hand-rolling std::process::Command.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search