Expand description
§shell-cmd-rs
shell-cmd-rs v1.3 — Recursively find files matching a regex and execute a shell command for each match.
This is a drop-in replacement for the C++20 shell-cmd utility, rewritten
entirely in Rust. It walks a directory tree, applies metadata filters (size,
modification time, permissions, ownership, type), substitutes placeholders in
a command template, and executes the resulting command for every matched entry.
§Features
- Regex-based file matching (via the
regexcrate) - Two regex modes:
- regex-search (default): matches if the pattern appears anywhere in the
full path (substring match via
Regex::is_match) - regex-match (
-z/--regex-match): the pattern must match the entire path (anchored with^(?:...)$)
- regex-search (default): matches if the pattern appears anywhere in the
full path (substring match via
- Glob mode (
-b/--glob): use familiar wildcard patterns (*,?) instead of regex — special characters are auto-escaped - Expression filter (
-f/--expr): composeglob(),regex(),regex_search(), andregex_match()predicates with boolean operatorsand,or,not, and parentheses - Placeholder substitution:
%0(filename),%1(full path),%b(stem),%e(extension),%2+(extra args); in--list-allmode%0expands to all matched paths joined by spaces - Metadata filters: size, modification time, permissions, owner, group, type
- Exclude patterns (regex by default, or glob via
-i/--glob-exclude), dry-run, verbose, confirm mode, stop-on-error - Parallel execution via
fork/execvwith proper signal handling - List-all mode (
-l/--list-all): collect all matches and run the command once with%0expanded to the full list of matched paths - Summary statistics (matched/run/failed)
§Architecture
The program flow is:
- Parse CLI arguments via
clapderive macros intoCli - Convert
CliintoOptions(runtime config) - Compile regex patterns
- In list-all mode (
-l), callfill_list()to collect all matches into a vector, then invokeproc_cmd()once with%0expanded to all paths - Otherwise, call
add_directory()to recursively walk the filesystem and callproc_cmd()per match to substitute placeholders and execute - In parallel mode, manage child PIDs via
CHILD_PIDSand drain withwait_all() - Print summary to stderr
§Signal Handling
Command execution uses system_cmd(), which mirrors the POSIX system()
behavior with proper SIGCHLD blocking and SIGINT/SIGQUIT ignoring in
the parent process. This prevents Ctrl+C from killing the batch runner while
allowing it to reach child processes.
Macros§
- error 🔒
- Print a colored error message to stderr. Prefixes the message with “Error: “ (bold red when color is enabled).
Structs§
- Cli 🔒
- Command-line interface definition using
clapderive macros. - Expr
Node 🔒 - AST node for expression-based file matching.
- Expr
Parser 🔒 - Recursive-descent parser for expression filter strings.
- Expr
Token 🔒 - Expr
Tokenizer 🔒 - Tokenizer for expression filter strings.
- Options 🔒
- Aggregated runtime options parsed from CLI arguments.
- Size
Filter 🔒 - Parsed size filter with comparison operator and byte threshold.
- Stats 🔒
- Execution statistics printed in the summary line.
- Time
Filter 🔒 - Parsed modification-time filter with comparison operator and day count.
Enums§
- CmpOp 🔒
- Comparison operator for size and time filters.
- Expr
Token 🔒Type - Token produced by the expression tokenizer.
- Expr
Type 🔒 - Node types for the expression filter AST.
Statics§
- CHILD_
PIDS 🔒 - Global pool of outstanding child process PIDs, used only in parallel mode
(
-j Nwhere N > 1). Protected by aMutexsince we access it from the main thread only (no actual concurrent access, but the Mutex satisfies Rust’sSend/Syncrequirements for statics). Initialized lazily on first access. - INTERRUPTED 🔒
- Global flag set to
truewhen SIGINT (Ctrl+C) is received. Checked alongsideSTOP_REQUESTEDto halt processing and exit cleanly. - STOP_
REQUESTED 🔒 - Global flag set to
truewhen--stop-on-erroris active and a command has failed. Checked at the top of each iteration inadd_directory()andproc_cmd()to halt processing early. UsesSeqCstordering since it is only written once and read from a single thread (parallel children don’t read it).
Functions§
- add_
directory 🔒 - entry_
matches_ 🔒path - Check whether a path matches the active search pattern or expression.
- fill_
list 🔒 - Recursively walk a directory and collect all matching file paths into
files. - gid_
to_ 🔒name - glob_
to_ 🔒regex - Convert a glob pattern to an equivalent regex string.
- main 🔒
- matches_
filters 🔒 - Test a directory entry’s metadata against all active filters.
- parse_
size_ 🔒filter - Parse a size filter string into a
SizeFilter. - parse_
time_ 🔒filter - Parse a time filter string into a
TimeFilter. - print_
help 🔒 - Program entry point.
- proc_
cmd 🔒 - Substitute placeholders in a command template and execute the result.
- replace_
all 🔒 - sigint_
handler 🔒 - SIGINT handler — sets the INTERRUPTED flag for clean exit.
- system_
cmd 🔒 - Execute a shell command via fork/exec with proper signal handling (mirrors the C++ System()).
- uid_
to_ 🔒name - use_
color 🔒 - Check whether to use color output on the given file descriptor. Respects the NO_COLOR environment variable convention (https://no-color.org/).
- wait_
all 🔒 - wait_
for_ 🔒slot