bashumerate: A programmable iterator for your shell

bashumerate: A programmable iterator for your shell

Overview

bashumerate provides a unified way to iterate over different sources using a single {} placeholder, avoiding the need to remember multiple loop or xargs syntaxes.

How to use

Basic usage follows the pattern enumerate <source-flag> <source> -- 'command {}'. The four built‑in source types are:

  • -f for files (e.g., enumerate -f '*.sh' -- 'wc -l {}')
  • -l for lines from a file (e.g., enumerate -l /etc/hosts -- 'echo {}')
  • -r for numeric ranges (e.g., enumerate -r 1 10 -- 'printf "n=%d\n" {}')
  • -L for explicit lists (e.g., enumerate -L a b c -- 'echo item: {}') If the command is omitted, items are printed to stdout and can be piped elsewhere.

Features

bashumerate offers:

  • Consistent syntax: the same {} placeholder works for files, lines, ranges, and lists.
  • Template mode: single‑quoted commands act as shell templates, allowing pipelines like enumerate -f '*' -- 'cat {} | head -5'.
  • Filters: --include and --exclude accept glob patterns to limit the source.
  • NUL‑safety: the -0 flag produces NUL‑delimited output for safe handling of spaces and special characters.
  • Extensibility: custom sources can be added by placing a file in lib/enumerators/ without any registration step.

Adding custom sources

To add a new source, create an executable file named enumerate_source_<name> inside lib/enumerators/. The function must output NUL‑terminated items. For example, a Docker source:

enumerate_source_docker() {
  docker ps --format '' | while read -r name; do
    printf '%s\0' "$name"
  done
}

After saving the file, the source is available as enumerate docker -- 'docker restart {}'.

Internals

The core implementation is about 140 lines of Bash. Internally, sources emit NUL‑delimited data, making the tool safe for filenames containing spaces or newlines. The {} placeholder is replaced at the shell level—no sed or eval tricks are used.

Installation

You can install bashumerate via Basher:

basher install wallach-game/bashumerate

Or manually clone the repository and symlink the binary:

git clone https://github.com/wallach-game/bashumerate.git
ln -s "$PWD/bashumerate/bin/enumerate" ~/.local/bin/

Naming

The name “bashumerate” is a concatenation of “bash” and “enumerate”, chosen because it directly describes what the tool does.

Community feedback

Comments on the Hacker News post show a mix of reactions:

  • Some users appreciate the idea:

    "I lol’d, but do you think this is valid? ... to me looks like a right solution, even if I was also tired of writing for loops by hand usually" — @giov4

  • Others question the necessity and point out existing alternatives:

    "I don’t want to be a downer here, but the structure of this repo and the verbosity+language of the docs feel 80% vibecoded." — @zxexz

  • Critics argue that the tool adds another syntax to learn:

    "Consistent syntax — same {} placeholder for files, lines, ranges, or lists. Inconsistent syntax native bash methods so its an additional syntax to learn." — @account42

  • Some note that null‑terminated approaches already exist in standard tools:

    "Or maybe you pipe into xargs and pray your filenames don’t have spaces … No need for prayer: find . -name '*.log' -print0 | xargs --null rm" — @tom_alexander

  • A few mention preferring established tools like GNU Parallel:

    "On the topic of xargs replacements, I love gnu parallel. The --dry-run flag of parallel made me confident to do more batch processing than I ever did with xargs." — @tester457

  • There are also remarks about the broader shell ecosystem:

    "In this thread: bash experts with arcane knowledge, unintentionally demonstrating how awful bash is." — @IdiotSavage Overall, the discussion reflects enthusiasm for reducing boilerplate as well as skepticism about adding yet another utility when established patterns already exist.

Sources