Turn the script into an observable contract
Define inputs, execution identity, environment, results, and failure states before writing the wrapper. A scheduler may use a different PATH, directory, and account from a terminal. Loading an administrator’s whole personal profile creates dependencies that are hard to reproduce. Make the executable, required configuration, and authorized destination explicit. If OUTPUT_DIR must be set and nonempty, a:? guard addresses that condition, but the path still needs validation. A zero exit code does not replace postconditions: producing 950 of 1000 expected records remains an incomplete delivery even if the tool considers execution finished.
Capture failure before replacing it
A pipeline has per-stage statuses and an aggregate status. With pipefail, the aggregate is the rightmost nonzero status in pipeline order, not the numerically largest error. PIPESTATUS exposes stages, but another command, including an assignment, can replace it. Capture evidence immediately. Use explicit results for critical decisions: set -e and ERR traps have conditional-context exceptions and do not form a transaction system. If a function used in an if fails at one stage and then ends with a successful printf, it may hide the error. Exercising that path matters as much as exercising success.
Preserve arguments, lines, and context
A path list is not a generic string. In Bash, quoted array expansion with @ preserves each element as an argument; quoted * joins elements. For lines with significant spaces and backslashes, use reading that preserves those data and handle end-of-file according to the contract. Names containing newline require a delimiter absent from filenames, such as NUL, across the entire tool chain. Redirection order also matters: duplicating stderr before or after redirecting stdout produces different destinations. Also establish whether a pipeline loop runs in a subshell before depending on its variables in the outer shell.
Coordinate jobs through the same object
Mutual exclusion needs a shared protocol. On a local filesystem using flock, participants must coordinate the same object and interpret lock conflict as a distinct state. Removing the name while a process holds the file open and recreating it can separate participants across different inodes. Both writing “lock acquired” in their logs no longer establishes coordination. Define whether a second job waits, fails, is deferred, or is skipped with a record. Also consider descriptors inherited by children and the actual filesystem guarantees; do not automatically transfer a local example to NFS or CIFS without establishing behavior.
Bound time and protect publication
A timeout bounds execution rather than undoing already-issued effects. If a remote request was sent and its response never arrived, query the operation identity before retrying. With GNU timeout, kill-after adds an interval after the initial signal; size the complete budget and do not promise functional termination merely because a signal was sent. Create temporaries with an operation that also reserves the object: mktemp -u only suggests a name. Tie publication to postconditions and the persistence contract taught in the filesystem module. Finally, a cleanup trap should preserve the relevant job status and handle its own failure without masking the outcome.
set +e
set -o pipefail
( exit 7 ) | ( exit 3 ) | cat
stage_status=( "${PIPESTATUS[@]}" )
printf "stage=%s\n" "${stage_status[@]}"Reading exercise: the three stages return 7, 3, 0. With pipefail, the aggregate is 3. Capturing PIPESTATUS immediately retains all three statuses; running another command first may lose that evidence. This example creates no files and sends no requests. Run this example in an isolated Bash: set +e makes continuation after the failed pipeline explicit; this is not a production wrapper.
Common pitfalls
Relying only on set -e; reading PIPESTATUS too late; exporting a variable expecting child-to-parent propagation; deleting active locks; using timeout as rollback; publishing merely because a file exists.
Related topics: Separate DNS, connections, TLS, and the application · Shell: arguments, pipelines, and exit codes · Isolate resources and establish readiness on Linux · Recover filesystems and publish data safely
Preserve each stage’s evidence and publish only when execution, content, concurrency, and acknowledgement satisfy the defined contract.
Reference: bash(1) · DR Linux 2026.4; networking and Bash manuals reviewed 2026-10-01; cgroup v2 and upstream systemd manuals reviewed 2026-10-01; RHEL 10 examples; Linux man-pages 6.19; OpenSSL 3.5