cloverleaf-larry/lib/each.sh
Bryan Johnson 3eb88f86c8 v0.4.1: each / each-site / len2nl / csv-to-table / table-to-csv
Five small Unix-style loop & format helpers, fully offline:

lib/each.sh
  - replaces v1 `each`
  - run a CMD per item: args, stdin lines, or {}-placeholder substitution
  - example: tbn adt | awk '{print $2}' | each.sh 'route_test {}'

lib/each-site.sh
  - replaces v1 each_site / each_site_hdr / each_site_tcl patterns
  - iterates every site under $HCIROOT with HCISITE/HCISITEDIR auto-exported
  - --filter REGEX limits which sites; --hdr prints a header before each

lib/len2nl.sh
  - replaces v1 `len2nl`
  - strict superset: handles length-prefixed (digits before MSH),
    MLLP (\x0b...\x1c\x0d), and segment CRs (→ LF)
  - works as stdin filter or with file arg

lib/csv-to-table.sh
  - 2-column CSV → Cloverleaf .tbl format
  - emits proper prologue (who, date, bidir, type, version)
  - --has-header --default VALUE --bidir 0|1 --in-delim CHAR --user NAME --out PATH

lib/table-to-csv.sh
  - reverse: .tbl → CSV
  - --with-header --delim CHAR --include-meta
  - confirmed clean round-trip: CSV → table → CSV byte-identical for the data rows

All 5 are pipeable, have --help, zero external deps beyond bash+awk+sed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 11:05:19 -07:00

46 lines
1.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# each.sh — run a command for each item. Replaces v1 `each`.
#
# Two input modes:
# each.sh <CMD> <ARG1> [ARG2 ... ARGN] — args as items
# <stream> | each.sh <CMD> — stdin lines as items (one per line)
#
# The literal token {} in CMD, if present, gets replaced with the current item.
# Otherwise the item is appended as the last arg.
#
# Examples:
# each.sh 'echo got:' /etc/hosts /etc/passwd
# tbn adt | awk 'NR>1{print $2}' | each.sh 'echo route_test for' {}
# nc-find.sh --process codametrix --format tsv \
# | awk -F'\t' 'NR>1{print $2}' \
# | each.sh 'lib/nc-msgs.sh' {} '--limit' '1' '--format' 'count'
#
# Each invocation is a separate process; failures are not aggregated. To
# stop on first error, prepend `set -e` in your own wrapper.
set -o pipefail
usage() { sed -n '2,20p' "$0"; exit 0; }
[ $# -ge 1 ] || usage
case "$1" in -h|--help) usage ;; esac
CMD="$1"; shift
run_one() {
local item="$1"
if [[ "$CMD" == *"{}"* ]]; then
eval "${CMD//\{\}/\"\$item\"}"
else
# Append item as last arg
eval "$CMD \"\$item\""
fi
}
if [ $# -gt 0 ]; then
for item in "$@"; do run_one "$item"; done
else
while IFS= read -r item; do
[ -z "$item" ] && continue
run_one "$item"
done
fi