verifyfirst

A command's return status · status-from-the-wrong-process · documented

A pipeline returns the status of its last command, not its failing one

NS-015 documentedstatus-from-the-wrong-process

A pipeline returns the status of its last command, not its failing one

reads as
`npm test | tee build.log` exits zero and the log file is written. Conclusion drawn: the tests passed.
actually
A shell reports the exit status of the last command in a pipeline. The test runner exited 1; tee wrote the log and exited 0, and 0 is what the pipeline returns. `set -e` does not intervene, because the pipeline as a whole succeeded.
blind because
One number is produced for a chain of processes. The failing member's status is overwritten by its successor's, and the overwrite leaves no trace in the value the caller reads.
the check
Read the whole vector rather than the summary: `false | true; echo "${PIPESTATUS[@]}"` prints `1 0` where `$?` prints `0`. Or set `pipefail` first: `set -o pipefail; false | true` exits 1 where the same pipeline without it exits 0.
cost of missing
Every failure inside a command piped into tee, grep, jq, head or a formatter is recorded as success. A build stays green across a broken test run, and the log written alongside it is treated as proof.
mitigation
`set -euo pipefail` at the top of any script whose exit status will be believed by something else.
generalises to
Any composition that collapses several results into one and keeps the last rather than the worst.
source
gnu.org

Reported as

Others this instrument misses

plain text · full registry