Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Shells: Exit-Status Mechanics Compared (SH-012)

The exit-status lifecycle ends at the shell: the shell’s wait-equivalent produces $?, applies the 128+N reporting convention, and decides what “failure” means for automation. POSIX XCU Chapter 2 fixes the baseline (§2.8.2 exit status, §2.9.2 pipelines, §2.9.3 lists, §2.11 signals/error handling), and each shell extends it differently. This hub compares the three shells documented in this part — bash, dash, zsh — feature by feature, with version notes, and links to the POSIX sections each row rests on.

Portability matrix

FeaturebashdashzshPOSIX baseline
Pipeline statusLast command (XCU 2.9.2); set -o pipefail → rightmost non-zero (manual §3.2.3; disabled by default; version attribution 3.0 unverified — see bash.md)Last command; set -o pipefail documented in current dash (verified in 0.5.13.4 manual); absent from POSIX and older dashLast command; setopt PIPE_FAIL (manual, options)Last command only; ! negation (2.9.2)
Per-element pipeline statuses$PIPESTATUS — uppercase, 0-indexed, transient (reset by nearly every command; manual §5.2)None — no array facility at all$pipestatus — lowercase, 1-indexed (0 under KSH_ARRAYS), <Z>: absent under sh/ksh emulationNone
$? semantics“exit status of the most recently executed command” (§3.7.5)“exit status of the most recent pipeline” (manual)“exit status returned by the last command” (manual params)“decimal exit status of the most recent pipeline” (2.5.2)
wait -n (wait for next job)Yes (manual §7.2; version attribution 4.3 unverified — see bash.md)No — verified: Illegal option -n, exit 2No — not in the documented builtin; wait takes jobspecs/PIDs, no-arg waits for allNot in POSIX
trap ERR / failure trapYes — ERR sigspec, exemptions mirror errexit (manual §4.1)No — POSIX sigspecs only (0/EXIT + signals)Yes, as ZERR (after each non-zero command); ERR is an aliasNot in POSIX (extension)
trap DEBUG / trap RETURNYes — before every simple command; after functions/.-scriptsNoYes — DEBUG (before/after per DEBUG_BEFORE_CMD), RETURN via TRAPRETURN/trapNot in POSIX
TRAPxxx function trapsNo (builtin only)NoYes — TRAPINT(){} etc.; own function environment; return-status semantics (zero = handled)Not in POSIX
EXIT trap scopeShell exit onlyShell normal exit only (“not via an unhandled signal”)Shell exit and function-body exit when set inside a function0/EXIT on shell exit (2.11-adjacent)
Trap survival in subshellsTrapped (non-ignored) signals reset on subshell creation; ignored-on-entry sticky (non-interactive)Same (manual: “resets trapped (but not ignored) signals to the default”; ignored-on-entry sticky)ZERR and DEBUG traps are kept within subshells; other traps reset (manual, builtins)Traps inherited; behavior of specific sigspecs not fixed (2.11)
Async SIGINT/SIGQUIT ignore (job control off)Yes — applied to the async subshell itself (verified: kill -INT on it is a no-op)POSIX 2.11 wording; verified: an async compound subshell died on SIGINT (rc 130) — boundary differs from bashYes per manual (Jobs & Signals) — plus the shell always ignores SIGQUITCommands in an async list inherit SIG_IGN for INT/QUIT (2.11)
errexit (-e) familyset -e; exemptions list (§4.3.1); default: -e cleared inside command substitutionset -e per POSIX wording; -e inherited into command substitutions (verified)ERR_EXIT; ignores contexts + **whole function body when called in &&/`
errexit into $( ) knobshopt -s inherit_errexit (on under POSIX mode; version attribution 4.4 unverified — see bash.md)n/a (always inherits — verified)No documented equivalentNot specified
Job control in scriptsMonitor off by default; no job notifications; SIGTTIN/SIGTTOU stops unreportedSameSame; monitor-mode CHLD trap fires per completed job (manual)set -m exists; scripts normally run with it off
Trapped-signal timingDeferred until foreground command completes; wait returns >128 then trap runs (§3.7.6)Same (POSIX 2.11 literal)Same baseline; TRAPNAL return status steers continue/interrupt2.11, verbatim

Version notes and honesty markers. All “verified” entries above come from the manuals fetched for this chapter and/or executed reproductions (bash 5.2.37, dash 0.5.13.4 in the research sandbox; zsh claims are manual-sourced — see each page). Three version attributions commonly recited could not be verified because bash’s NEWS/changelog was unreachable from the sandbox: pipefail = bash 3.0, wait -n = bash 4.3, inherit_errexit = bash 4.4; the features themselves are verified present in the current manual (bash.md marks each inline). Similarly, dash’s pipefail is verified in the 0.5.13.4 manual; the introduction release is not verified here.

Reading guide

  • bash.md (SH-001–SH-006) — pipeline/pipefail, PIPESTATUS, substitution and subshell propagation, the full trap surface, wait/wait -n/jobs, set -e+ERR+inherit_errexit.
  • dash.md (SH-007–SH-008) — the POSIX-strict baseline: last-command pipelines, current-dash pipefail, no status array, wait -n absent, POSIX-only traps, async-signal boundary differences.
  • zsh.md (SH-009–SH-011) — PIPE_FAIL + lowercase 1-indexed pipestatus, TRAPNAL functions and their return-status semantics, subshell-keeping ZERR/DEBUG traps, ERR_EXIT’s documented deviations, wait with jobspecs.

POSIX anchors

References