How to check a command's exit code in Bash

Check the exit code of the last command in Bash with $?, in a condition with if, and for a whole pipeline with PIPESTATUS. Copy-paste examples.

Tldr

Read `$?` immediately after a command to get its exit status (0 = success, non-zero = failure). Prefer testing directly with `if command; then` over capturing `$?` separately, since any command in between overwrites `$?`. For pipelines, use `${PIPESTATUS[@]}` to see every stage's exit code.

Intro

Exit codes are how Unix commands report success or failure, and Bash gives several ways to inspect them, each suited to a different situation: a single command, a pipeline, or a background job.

Steps

Name

Check $? right after a command

Text

$? always holds the exit status of the most recently completed foreground command. It gets overwritten by the very next command, including things like [ ] or echo, so use it immediately.

Name

Prefer testing the command directly in an if

Text

This avoids the $? footgun entirely — Bash uses the command's exit status as the condition, and you never need a separate variable.

Name

Check exit codes across a pipeline

Text

By default $? after a pipeline only reflects the last command. Use PIPESTATUS (a Bash array) to see the exit status of every stage.

Name

Fail a pipeline if any stage fails

Text

set -o pipefail makes the overall pipeline exit status equal to the last non-zero exit code among all stages, instead of always following the last command.

Name

Check the exit code of a background job

Text

wait returns the exit status of the job it waits on, which is how you collect results from background processes.

Faq

Q

What exit code means success?

A

0 means success by Unix convention. Any non-zero value (1-255) indicates some kind of failure, though the specific meaning of each non-zero code is command-specific.

Q

Why did $? show 0 even though a command in my pipeline failed?

A

Without `set -o pipefail`, a pipeline's exit status is only the exit status of its last command. Check ${PIPESTATUS[@]} for individual stages, or enable pipefail to propagate any failure.

Q

Does $? work after an if statement or function call?

A

Yes, $? reflects the exit status of the last command executed inside the if/function, or the exit status of the if construct itself if you check it right after the whole block.