A programmable iterator for bash — enumerate files, lines, ranges, and lists, then pipe or exec commands on each item.
# basher
basher install wallach-game/bashumerate
# manual
git clone https://github.com/wallach-game/bashumerate.git
ln -s "$PWD/bashumerate/bin/enumerate" ~/.local/bin/enumerate -f <glob>... -- <command>
enumerate -l <file> -- <command>
enumerate -r <start> <end> -- <command>
enumerate -L <item>... -- <command># List files
enumerate -f '*.sh'
# Count lines per file
enumerate -f '*.sh' -- 'wc -l {}'
# Iterate over lines from stdin
cat urls.txt | enumerate -l - -- 'curl -s {}'
# Numeric range
enumerate -r 1 10 -- 'printf "%02d\n" {}'
# Pipe to xargs
enumerate -f '*.log' | xargs rm
# Filter results
enumerate --include '*.md' --exclude 'README*' -f .The {} placeholder is replaced with each item.
Use -- to separate source args from the command.
| Flag | Source | Args | Description |
|---|---|---|---|
-f, --files |
files | glob... |
Enumerate files matching glob pattern(s) |
-l, --lines |
lines | file or - |
Enumerate lines from a file or stdin |
-r, --range |
range | start end |
Enumerate integers from start to end |
-L, --list |
list | item... |
Enumerate literal items |
| Flag | Description |
|---|---|
--include <glob> |
Only include items matching glob |
--exclude <glob> |
Exclude items matching glob |
-0, --null |
NUL-delimited input/output |
-n, --dry-run |
Print commands without executing them |
-p, --parallel |
Execute commands in parallel |
-P, --max-procs N |
Limit parallel jobs (default: number of CPUs) |
-s, --speed |
Speed mode — inline execution, skip filters/dry-run/parallel |
-q, --quiet |
Suppress error messages |
--help |
Show help |
Drop a file in lib/enumerators/foo.sh containing enumerate_source_foo():
# lib/enumerators/docker.sh
enumerate_source_docker() {
docker ps --format '{{.Names}}' | while read -r name; do
printf '%s\0' "$name"
done
}enumerate docker -- 'docker restart {}'enumerate is significantly faster than xargs -I{} and find -exec for per-item command execution, thanks to streaming (no temp file I/O), eval-based execution (no subprocess per item), and an optimized inlined speed mode (-s).
Benchmark results on AMD Ryzen 9 5900X (24 cores):
| Test | Items | xargs -I{} | enumerate | enumerate -s | vs xargs |
|---|---|---|---|---|---|
| echo per item | 1000 | 710ms | 59ms | 26ms | 12-27x |
| echo per item | 10000 | 6.07s | 484ms | — | 12.5x |
| file listing | 10000 | 7.16s | 623ms | — | 11.5x |
| wc per item | 10000 | — | — | comparable | for loop w/ ext cmd is 11x slower |
For synthetic no-op benchmarks (:), enumerate -s is ~17x slower than a bare for loop (down from ~1500x before optimizations). For any real command (echo, wc, curl, etc.), the overhead is negligible — the command's own execution time dominates.
bash bench/bench.shbasher install bats-core/bats-core
bats test/MIT