Crate shell_cmd_rs

Crate shell_cmd_rs 

Source
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 regex crate)
  • 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 ^(?:...)$)
  • Glob mode (-b/--glob): use familiar wildcard patterns (*, ?) instead of regex — special characters are auto-escaped
  • Expression filter (-f/--expr): compose glob(), regex(), regex_search(), and regex_match() predicates with boolean operators and, or, not, and parentheses
  • Placeholder substitution: %0 (filename), %1 (full path), %b (stem), %e (extension), %2+ (extra args); in --list-all mode %0 expands 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/execv with proper signal handling
  • List-all mode (-l/--list-all): collect all matches and run the command once with %0 expanded to the full list of matched paths
  • Summary statistics (matched/run/failed)

§Architecture

The program flow is:

  1. Parse CLI arguments via clap derive macros into Cli
  2. Convert Cli into Options (runtime config)
  3. Compile regex patterns
  4. In list-all mode (-l), call fill_list() to collect all matches into a vector, then invoke proc_cmd() once with %0 expanded to all paths
  5. Otherwise, call add_directory() to recursively walk the filesystem and call proc_cmd() per match to substitute placeholders and execute
  6. In parallel mode, manage child PIDs via CHILD_PIDS and drain with wait_all()
  7. 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 clap derive macros.
ExprNode 🔒
AST node for expression-based file matching.
ExprParser 🔒
Recursive-descent parser for expression filter strings.
ExprToken 🔒
ExprTokenizer 🔒
Tokenizer for expression filter strings.
Options 🔒
Aggregated runtime options parsed from CLI arguments.
SizeFilter 🔒
Parsed size filter with comparison operator and byte threshold.
Stats 🔒
Execution statistics printed in the summary line.
TimeFilter 🔒
Parsed modification-time filter with comparison operator and day count.

Enums§

CmpOp 🔒
Comparison operator for size and time filters.
ExprTokenType 🔒
Token produced by the expression tokenizer.
ExprType 🔒
Node types for the expression filter AST.

Statics§

CHILD_PIDS 🔒
Global pool of outstanding child process PIDs, used only in parallel mode (-j N where N > 1). Protected by a Mutex since we access it from the main thread only (no actual concurrent access, but the Mutex satisfies Rust’s Send/Sync requirements for statics). Initialized lazily on first access.
INTERRUPTED 🔒
Global flag set to true when SIGINT (Ctrl+C) is received. Checked alongside STOP_REQUESTED to halt processing and exit cleanly.
STOP_REQUESTED 🔒
Global flag set to true when --stop-on-error is active and a command has failed. Checked at the top of each iteration in add_directory() and proc_cmd() to halt processing early. Uses SeqCst ordering 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 🔒