[CODING_BASH]
All code follows these governing principles:
- [FUNCTIONAL] — immutable locals, pure functions, dispatch tables, and tightly bounded mutable shell state
- [POLYMORPHIC] — one parser, one dispatcher, one logger; extend via table entries not code branches
- [PRODUCTION_HARDENED] — ERR traps, atomic I/O, signal forwarding, cleanup registries, version gating
- [FORK_MINIMAL] —
printf -v,$(<file),EPOCHSECONDS, fork-free${ },BASH_MONOSECONDStiming,mapfileover subshell patterns - [ECOSYSTEM_FIRST] —
rg/fd/jq/sd/choose/mlrover sed/grep/find/cut when available - [EXECUTABLE_DOCTRINE] — examples and templates must pass syntax, ShellCheck, and their own self-tests
[01]-[ROUTING]
[FOUNDATION]: every task loads bash-scripting-guide.md — primitives, strict mode, expansion, arrays.
[TASK_ROUTED]: load only when the task matches.
- [01]-VERSION_FEATURES: runtime features, fork-free substitution, version gating
- [02]-VARIABLE_FEATURES: call stacks, namerefs, traps, process lifecycle, runtime variables
- [03]-ARRAY_OPERATIONS: set algebra, structural transforms, higher-order traversal
- [04]-STRING_OPERATIONS: transform pipelines, regex extraction, codecs, templates
- [05]-FILE_OPERATIONS: atomic writes, FD multiplexing, directory traversal
- [06]-SCRIPT_PATTERNS: arg parsing, help, ERR traps, parallel, retry
- [07]-BASH_LOGGING: structured logging, CI integration, tracing
- [08]-BASH_TESTING: bats-core suites, coverage, hypothesis PBT
- [09]-BASH_PORTABILITY: cross-shell compat, containers, POSIX
- [10]-TEXT_PROCESSING_GUIDE: rg/awk/sd/jq/yq/mlr tool selection
- [11]-VALIDATION: ShellCheck codes, static analysis, CI
[EXAMPLES]: read the one matching the target archetype before writing.
- [01]-CLI_TOOL: two-dimensional verb:resource dispatch CLI
- [02]-DATA_PIPELINE: file processing with jq pipelines, accumulation
- [03]-SERVICE_WRAPPER: container entrypoint, signal dispatch, coproc
[TEMPLATE]: scaffold a new script from standard.template.md — strict-mode header, logging, cleanup stack, traps, flag parsing, self-test.
[02]-[PARADIGM]
- [IMMUTABILITY]:
local -rfor every non-mutating function local,readonlyfor module-level constants - [FROZEN_ARGS]: Mutable state only for argument parsing — frozen via
readonlyin_mainbefore core logic - [DISPATCH_TABLES]:
declare -Arfor O(1) command routing and two-dimensionalverb:resourcedispatch - [DATA_TABLES]:
declare -Aralso tables option metadata, validation rules, log-level gating, and env contracts (regex per var) - [CASE_RESERVED]:
case/esacreserved exclusively for glob/regex pattern matching — never conditional routing - [PURE_FUNCTIONS]: Input via positional parameters, output via stdout or nameref (
local -n); no global reads exceptreadonlyconstants - [SIDE_EFFECTS]: Side effects isolated to
_main, trap handlers, cleanup registries, and explicitly marked shell boundary loops - [METADATA_DRIVEN_HELP]:
_OPT_METAwithshort|long|desc|VALUE_NAME|defaultentries generates_usage. One entry + onecasebranch per option - [MIDDLEWARE_COMPOSITION]:
_use()registers middleware into_MIDDLEWARE;_run_with_middleware()executes the chain before handler dispatch - [PARSE_PHASES]: Argument parsing composes subcommand dispatch (O(1) table lookup), flag parsing (
case/esac), positional collection - [EXPRESSION_OVER_STATEMENT]:
${var:-default}over if-empty,${var:?message}over assert-nonempty,(( expr ))overtest, expansion over forks - [FORK_ELIMINATION]:
printf -v var '%(%F %T)T' -1over$(date),$(<file)over$(cat file),EPOCHSECONDS/EPOCHREALTIMEover$(date +%s),mapfileoverwhile read,BASH_REMATCHovergrep -oP - [ATOMIC_IO]: Every file write goes
mktemp+ write +mv— rename is atomic on the same filesystem;umask 077beforemktempfor sensitive data - [DYNAMIC_FD]: Dynamic FDs via
exec {fd}>file
[03]-[CONVENTIONS]
[ECOSYSTEM_TOOL_SELECTION]: richer ecosystem tools own each available operation:
| [INDEX] | [TASK] | [PREFERRED] | [FALLBACK] | [NEVER] |
| :-----: | :--------------- | :---------- | :----------------- | :-------------------- |
| [01] | File search | fd | find | ls -R |
| [02] | Content search | rg | grep -rn | find -exec grep |
| [03] | JSON | jq | python3 -c | sed/awk on JSON |
| [04] | YAML | yq eval | python3 -c | sed on YAML |
| [05] | CSV/TSV | mlr | awk -F | cut for multi-field |
| [06] | Stream edit | sd | sed | awk for simple sub |
| [07] | Column select | choose | awk '{print $N}' | cut -d |
| [08] | Interactive JSON | jnv | jq | -- |
[SELECTION_RULES]:
- Probe availability via
command -vbefore use; fall back gracefully rgandfdown repo-aware searches through default.gitignoreintegrationjqis mandatory for JSON — never parse JSON with sed/awk/grepmlrhandles format conversion (CSV to JSON, TSV to JSON) natively- Pipeline preference: single
awkprogram over chainedgrep | sed | cut
[04]-[CONTRACTS]
[VARIABLE_DISCIPLINE]:
local -rfor all non-mutating function locals.readonlyfor all module-level constants.- Mutable state (parsed args, log level) declared at module level, frozen via
readonlyin_mainbefore core logic. declare -Arfor all dispatch tables, option metadata, and lookup maps.local -n(nameref) for passing arrays to functions — neverevalor indirect expansion.- Nameref return channels: a scalar-returning function takes the result var last (
_ext "$item" key), writing viaprintf -v "$2"orlocal -n. - Callers pass a name, never
$(). Multi-return via multiple namerefs (_project_meta "$slug" name created). - Naming:
UPPER_SNAKEfor constants/env,lower_snakefor locals/functions,_prefix for internal functions.
[CONTROL_FLOW]:
case/esacfor pattern matching (globs, regexes) only — never for if/elif-style routing.declare -Ardispatch tables for command routing:"${_DISPATCH[${cmd}]}" "${args[@]}". Nest for subdomains:_CONFIG_SUBCMDS,_INIT_SUBCMDS.[[ ]]over[ ].(( ))for arithmetic.&&/||for short-circuit.mapfile -t/readarray -d ''overwhile readloops for collection.- Streaming consumers may use
while IFS= read -rwith a comment naming the stream boundary. - Ternary via arithmetic:
(( condition )) && action1 || action2or${var:+if_set}${var:-if_unset}. - Bounded concurrency:
wait -n -p finished_pidwith job-count gate(( ${#jobs[@]} >= MAX_JOBS ))— see_run_poolpattern in examples. - Shell reality exceptions are explicit: option parsing uses
case; cleanup stacks may use a staticevaltemplate over shell-quoted commands. - Bounded counters and polling loops may mutate when the mutation is the resource protocol.
[ERROR_HANDLING]:
set -Eeuo pipefail+shopt -s inherit_errexitin every script. No exceptions.- ERR trap with
BASH_COMMAND,BASH_LINENO,FUNCNAMEcontext. Stack trace for multi-level call chains. _CLEANUP_STACKLIFO registry invoked by EXIT trap._CLEANINGguard prevents re-entrant execution on cascading signals.- Exit codes: 0=success, 1=general error, 2=usage error. Custom codes in
EX_*constants. _die()for fatal errors (log + exit)._die_usage()for argument errors (log + hint + exit 2).- Timing:
BASH_MONOSECONDSfor elapsed-time durations — monotonic, immune to NTP drift, zero forks. EPOCHREALTIMEonly for absolute timestamps and microsecond benchmarks (_bench()shape:(end_s - start_s) * 1000000 + 10#end_us - 10#start_us).- Version-safe
BASH_MONOSECONDS/EPOCHREALTIMEfallback dispatch:references/version-features.md.
[LOGGING_ARCHITECTURE]:
declare -Ar _LOG_EMIT=([json]=_log_json [text]=_log_text_emit)tables the emitters.- Format resolves once at startup:
readonly _LOG_EMITTER="${_LOG_EMIT[${LOG_FORMAT:-text}]}". _log()gates on_LOG_LEVELSnumeric threshold, then dispatches via"${_LOG_EMITTER}"— zero branching per call.- JSON emitter:
jq -nc --argfor injection-safe serialization withEPOCHREALTIMEmicrosecond timestamps and optional W3C trace context fields. FUNCNAMEoffset accounts for_info->_log->_LOG_EMITTERcall chain depth (typicallyFUNCNAME[3],BASH_LINENO[2]).
[SURFACE]:
_prefix for all internal functions. Public surface =_mainentry point only.- One dispatch table per concern — extend by adding entries, not code branches.
- No utility/helper files — colocate all logic in the script.
sourceonly for test frameworks. --self-testflag runs embedded smoke tests and exits — validates dispatch tables, config parsing, and key pure functions.- ~350 LOC scrutiny threshold — investigate compression via dispatch tables and awk programs, not file splitting.
[RESOURCES]:
- Temporary files:
mktemp+_register_cleanup "rm -f -- $(printf '%q' "${tmp}")"or an equivalent static quoted cleanup template. - Work directories:
mktemp -dwithSRANDOMin path for uniqueness. - Signal forwarding for PID 1: trap TERM/INT,
kill -"${sig}" "${_CHILD_PID}", exit with signal code (143/130). Guard on(( _CHILD_PID > 0 )). BASH_TRAPSIGcarries the signal number, so one unified handler routes every signal through a dispatch table.GLOBSORTcontrols glob ordering (e.g.,-mtimefor newest-first file discovery).- Retry:
_retry_exec max delay max_delay cmd...— backoffdelay=$(( delay * 2 > max_delay ? max_delay : delay * 2 ))withSRANDOMjitter. - Env contracts:
declare -Ar _ENV_CONTRACT=([VAR]='^regex$')validated at startup — dispatch table over env vars, regex per key. - Health endpoint:
socat TCP-LISTEN:${port},reuseaddr,fork SYSTEM:"printf 'HTTP/1.1 200 OK\r\n...'"backgrounded with cleanup registration. - W3C tracing: parse
TRACEPARENTviaBASH_REMATCH, generate viaprintf -v TRACE_ID '%08x%08x%08x%08x' "${SRANDOM}"..., export for children.
[05]-[ANTI_PATTERNS]
[STATE_VIOLATIONS]:
- MUTABLE STATE:
let/global mutation outsidedeclare -gconfig loading. Uselocal -r/readonly; freeze parsed args in_main. - FORK IN HOT PATH:
$(date),$(cat file),$(wc -l < file)in loops. Useprintf -v,$(<file),EPOCHSECONDS,mapfile.
[CONTROL_FLOW_VIOLATIONS]:
- IMPERATIVE DISPATCH:
if/elif/elsechain for command routing. Usedeclare -Ardispatch table + O(1) lookup. - WHILE-READ COLLECTION:
while IFS= read -r lineloop to build arrays. Usemapfile -t arr < <(cmd). - UNMARKED STREAM LOOP:
while readwithout a streaming-boundary comment. Streaming consumers are valid; collection loops are not. - NAKED WRITE: Direct
>or>>for output files. Usemktemp+mvatomic pattern.
[SAFETY_VIOLATIONS]:
- HARDCODED FD:
exec 3>filewith literal FD numbers. Useexec {fd}>filefor safe dynamic allocation. - UNQUOTED EXPANSION:
$varwithout quotes. Always"${var}"— exceptions only in(( ))arithmetic. - EVAL INJECTION:
eval "$user_string"with untrusted input. Usedeclare -Ardispatch orcase/esacpattern match. - ECHO OVER PRINTF:
echo -e/echo -nfor formatted output. Useprintf— portable, no ambiguity, format strings.
[ORGANIZATION_VIOLATIONS]:
- UTILITY EXTRACTION:
lib/utils.sh,common.shhelper files. Colocate all logic in the script. - RANDOM OVER SRANDOM:
$RANDOMfor security-relevant randomness (temp names, jitter, tokens). Use$SRANDOM(cryptographic entropy).
[06]-[VALIDATION_GATE]
- Required:
bash -n script.sh(syntax check), ShellCheck clean (static analysis). - Required for executable examples: run
--self-testwhen present. - Reject completion when strict mode, readonly discipline, ShellCheck compliance, or example self-tests are not satisfied.
[07]-[FIRST_CLASS_TOOLS]
| [INDEX] | [TOOL] | [PROVIDES] |
| :-----: | :----------- | :----------------------------------------- |
| [01] | bash | Shell runtime, builtins, ${ } |
| [02] | shellcheck | Static analysis, SC codes |
| [03] | bats-core | Test framework, TAP output |
| [04] | kcov | Coverage instrumentation |
| [05] | rg | Content search, .gitignore-aware |
| [06] | fd | File search, .gitignore-aware |
| [07] | jq | JSON processing, streaming, trim, skip |
| [08] | yq | YAML processing |
| [09] | mlr | CSV/TSV/JSON format transforms |
| [10] | sd | Stream editing (sed replacement) |
| [11] | choose | Column selection (cut replacement) |