Course outline

Bash / Intermediate: reliable data and failure paths / Lesson 7

Design pipeline status handling deliberately

Separate data flow from status flow, inspect PIPESTATUS immediately, and understand errexit exceptions without treating them as a complete error policy.

About 4 min readContent reviewed
On this page

Lesson goals

  • Explain default and pipefail pipeline results.
  • Capture individual statuses before another command replaces them.
  • Handle expected nonzero results and errexit contexts explicitly.

A pipeline carries bytes and several completion results

The pipe connects standard output of one command to standard input of the next; standard error remains separate unless explicitly redirected. Each stage has its own completion status, while Bash also calculates a pipeline status. The default is the last stage’s result. A producer can fail after emitting partial output while the consumer completes normally. Therefore a valid-looking report is not proof the whole transformation succeeded. Decide whether partial output may be consumed, how diagnostics are preserved, and which stage failures are acceptable before choosing a short pipeline expression.

pipefail changes aggregation, not interpretation

With pipefail enabled, Bash reports the rightmost nonzero stage status, or zero if all succeed. It does not mean the first failure, and it does not distinguish a documented no-match status from an actual error. A deliberately short-reading consumer can cause an upstream SIGPIPE after it has received enough data, which needs interpretation in that pipeline’s contract. pipefail is a Bash capability whose portability must be checked for the intended sh environment rather than assumed. Keep option changes scoped when a reusable helper must not alter its caller’s policy.

bash Compare two pipeline aggregation policies
set +e
false | true
printf 'default=%s\n' "$?"
set -o pipefail
false | true
printf 'pipefail=%s\n' "$?"
Expected output
default=0
pipefail=1

The controlled commands produce no data and deliberately return different statuses. The example demonstrates aggregation without involving files or a real failing application.

Capture the stage vector immediately

PIPESTATUS is Bash’s array of statuses from the most recently executed foreground pipeline. Even a following simple command can replace it. Capture the entire array in one assignment immediately after the pipeline, then inspect the saved copy. Printing $? first destroys the original stage vector. Likewise, separate assignments to save each element do not preserve one consistent observation. Use named stage positions in the script’s documentation so an added filter does not silently change which index means producer or validator. Individual statuses still need interpretation using the respective program’s documented contract.

bash Preserve two completion results
set +e
{ printf 'partial\n'; false; } | { while IFS= read -r line; do :; done; }
stages=("${PIPESTATUS[@]}")
printf 'producer=%s consumer=%s\n' "${stages[0]}" "${stages[1]}"
Expected output
producer=1 consumer=0

The producer emits synthetic partial data and fails. The consumer drains it successfully; saving PIPESTATUS exposes the difference even though the default pipeline result would be zero.

errexit follows grammatical contexts

set -e is suppressed in documented contexts such as tested conditions and certain list or pipeline positions. Calling a function as an if condition can cause its internal commands to run without the exit behavior expected from a normal invocation. Command substitution has additional inheritance rules, including POSIX mode and Bash’s inherit_errexit option. These details are version and context sensitive, so test the exact control structure. ERR traps have related exclusions and inheritance options; they do not turn every failed command into a universal exception. Explicitly handle operations whose failure affects correctness.

Design success around required stages

A robust transform names required producers, optional searches, validators, and publication steps. Keep output in a separate lab destination until required stages and validation succeed; do not expose partial output as a completed artifact. Preserve the original failure and report the failing stage without replacing data with diagnostics. Test producer failure, consumer failure, no-match, and early termination separately. A pipeline that is convenient interactively may need a clearer multi-step design in a maintained script. Use pipefail as one useful input to that policy rather than its entire definition.

Common errors and fixes

A successful consumer hides producer failure

Why it happens
Only the default final-stage status was checked.
How to fix it
Use an explicit required-stage policy and immediate statuses.
Prevent it next time
Test partial-output failures.

PIPESTATUS is read after logging

Why it happens
The log command replaced the vector.
How to fix it
Save the complete array before any other command.
Prevent it next time
Keep capture adjacent to the pipeline.

set -e is assumed to stop every function failure

Why it happens
A tested invocation changes errexit behavior.
How to fix it
Handle required commands explicitly inside the function.
Prevent it next time
Test the exact call context, not just the body.

Check your understanding

1. Why is printf "$?" followed by reading PIPESTATUS unreliable?

Show worked solution

printf itself becomes the most recent foreground command and replaces the stage vector. Save PIPESTATUS in one array assignment first; derive the needed decision from that saved observation rather than issuing an intervening command.

2. A search returns no matches inside a required pipeline. Must that always abort?

Show worked solution

Only if the application contract requires at least one match. Distinguish expected absence from a search error and define the branch deliberately. pipefail exposes a nonzero result but cannot decide the business meaning of that status.

Key takeaways

  • Capture stage results before running anything else.
  • Treat errexit as contextual behavior and design explicit failure paths.

Official references