Use when writing or modifying bash scripts (launch-phase.sh, verify.sh, full-verify.sh, CI scripts). Covers pipe exit codes, heredoc quoting, set flags, and common gotchas.
Utility skill — shared foundation used by orchestration, hooks, e2e, and deployment skills.
$? in a pipeline returns the LAST command's exit code. Use ${PIPESTATUS[0]} for the
first command, ${PIPESTATUS[1]} for the second, etc.
cmd1 | tee log.txt
echo ${PIPESTATUS[0]} # cmd1's exit code, not tee's
<<'DELIM' (single-quoted): prevents ALL variable expansion inside the heredoc. Use for wrapper scripts executed later.<<DELIM (unquoted): expands variables at write time. Use \$ to escape variables that should expand at runtime.set -uo pipefail — always use in scripts
pipefail: pipes return the first non-zero exit codeset -u: catches undefined variablesset -e in scripts with intentional non-zero exits (like retry loops)When generating a script file with cat <<'DELIM', use placeholder strings and
sed -i 's|PLACEHOLDER|value|g' to inject values — avoids quoting hell.
If a function returns a value via echo, ALL other output inside it must go to
>/dev/null or >&2. Stray output corrupts the return value.
my_func() {
git checkout -b feature >/dev/null 2>&1 # suppress output
npm install >&2 # redirect to stderr
echo "/path/to/result" # only this goes to stdout
}
result=$(my_func)
script -q -c wraps a command in a pseudo-TTY for logging but is fragile across platformscmd 2>&1 | tee -a logfile with PIPESTATUS for exit code capturePiping a long-running process through head, less, or any command that closes early
sends SIGPIPE to the writer, killing it silently:
# DANGEROUS — kills the script after head reads 30 lines:
./my-long-script.sh | head -30
# SAFE — capture full output, read later:
./my-long-script.sh 2>&1 | tee output.log
tail -30 output.log
This is especially dangerous with orchestration scripts that manage child processes — SIGPIPE kills the parent, orphaning all children.
Always run bash -n scriptname.sh after editing any bash script to catch syntax errors
before committing.
cd with && — if a later command fails, the cd does not persist and subsequent calls run in the wrong directory.grep -oP is not portable. Use sed or node -e instead.PIPESTATUS immediately — it's overwritten by the next command.((var++)) returns exit code 1 when var is 0 (because ((0)) is falsy in bash). Under set -e this kills the script. Use var=$((var + 1)) instead./dev/stdin must passthrough ENXIO/EAGAIN/ENOENT errors via process.exit(0) — these are infrastructure failures (stdin unavailable), not logic errors. Fail-closed on infrastructure errors bricks the entire session with no self-recovery path.