A command can print reassuring output and still fail. Another command can finish silently and succeed. The shell does not decide from the words on the screen. It reads a small numeric result called an exit status.
Every command returns an exit status when it finishes. In Bash,
0 means success and any non-zero value means the command did
not complete successfully. The special parameter $? contains
the status of the most recently completed foreground command.
Operators such as && and ||, conditional
statements such as if, and automation tools use that status
to decide what should happen next.
- What You’ll Learn
- What Is a Linux Exit Code?
- How to Check an Exit Code with $?
- Read the status before another command replaces it
- What Common Exit Statuses Mean
- How && and || Use Exit Status
- Do not use && and || as a casual replacement for every if
- Exit Status and Pipelines
- How Scripts Return a Result
- Functions use return
- Mini Lab: Watch the Shell Make Decisions
- Common Exit Code Mistakes
- Knowledge Check
- Official References
What You’ll Learn
- What an exit code tells the shell.
- How to inspect a result with
$?. - Why zero means success in Unix-like systems.
- How
&&and||react to success and failure. - How pipelines affect the status you receive.
- How scripts return useful results to their callers.
What Is a Linux Exit Code?
An exit code, more precisely called an exit status, is the numeric result a command returns when it finishes. It travels separately from the command’s standard output and standard error.
That separation matters. Output is information for a person or another program to read. An exit status is a compact decision signal for the shell. If standard streams are unfamiliar, read Linux Standard Streams Explained first.
A non-zero result does not always mean that the computer is broken. A search command may use one status for “a match was found,” another for “no match was found,” and another for an actual error. The command’s documentation defines those meanings.
0 as “yes, that
worked.” Treat a non-zero value as “do not assume success; inspect what this
command says the value means.”
How to Check an Exit Code with $?
Run a command, then print $? immediately:
true
printf '%s\n' "$?"
false
printf '%s\n' "$?"
The true builtin returns 0. The
false builtin returns a non-zero status. They perform almost no
visible work, which makes them useful for learning control flow.
Read the status before another command replaces it
$? is temporary. Each new foreground command produces a new
status, including the command you use to display the previous one.
false
status=$?
printf 'The saved status is %s\n' "$status"
Saving the value in a variable preserves the result you care about. Quoting
"$status" is a good habit when expanding shell variables. For
the wider relationship between variables and child programs, see
Linux Environment Variables Explained
.
$?, you inspect the diagnostic command’s result instead of the
original result.
What Common Exit Statuses Mean
The range available to Bash is 0 through 255, but
there is no universal dictionary for every value. Programs are free to
document their own statuses. A few values have special meaning in Bash
itself.
| Status | General meaning | What to remember |
|---|---|---|
0 |
Success | The shell treats the command as successful. |
1 |
Often a general failure or a command-specific result | Check that command’s manual; do not assume one universal meaning. |
2 |
Incorrect use for Bash builtins; other tools may define it differently | Context and documentation still matter. |
126 |
Command found but could not be executed | Inspect permissions, file format, and the execution environment. |
127 |
Command not found | Check spelling and PATH. |
128 + N |
Command ended because of fatal signal N |
Use the signal number to investigate how the process ended. |
For example, status 127 can point toward a missing command or a
PATH problem, while a signal-derived status may connect the
failure to process control. The lessons on the
Linux shell
and
Linux processes and signals
provide the surrounding model.
How && and || Use Exit Status
These operators do not inspect output text. They decide whether to run another command by reading the status of the command on their left.
| Form | When the right side runs | Plain-language reading |
|---|---|---|
first && second |
Only if first succeeds |
Do the second step after a successful first step. |
first || second |
Only if first returns non-zero |
Use the second step as a failure path. |
! command |
The command runs normally, then its logical result is inverted | Treat success as false and failure as true. |
mkdir -p demo && cd demo
test -f settings.conf || printf 'settings.conf is missing\n'
In the first line, cd demo runs only if the directory operation
succeeds. In the second, the message appears only when the file test returns
non-zero.
Do not use && and || as a casual replacement
for every if
Short chains are readable. More complicated decisions become clearer with an explicit conditional:
if test -r settings.conf; then
printf 'Configuration is readable\n'
else
printf 'Configuration is missing or unreadable\n' >&2
fi
The code after then runs when the test returns 0.
The else branch runs when it returns non-zero. This is the
bridge from individual commands to understandable shell scripts.
Exit Status and Pipelines
A pipeline connects the output of one command to the input of another:
printf '%s\n' alpha beta gamma | grep -q beta
In Bash, the status of a pipeline is normally the status of its
last command. Here, the pipeline reports the result from
grep -q beta.
That default can hide an earlier failure. If the first command fails but the
last command succeeds, the pipeline may still return success. Bash provides
the pipefail option when a script needs earlier pipeline
failures to affect the result:
set -o pipefail
producer | processor | consumer
status=$?
With pipefail disabled, the last command determines the status.
With it enabled, a failing command earlier in the pipeline can make the
pipeline fail. Read the complete stream model in
the lesson on redirection and pipes
.
pipefail
changes how a script interprets pipelines. Enable it because your script’s
error policy requires it, not because it appeared in a copied snippet.
How Scripts Return a Result
A shell script is itself a command from its caller’s point of view. If the
script reaches its end, its status is normally the status of the last
command it executed. The exit builtin lets the script report a
deliberate value.
if test ! -r settings.conf; then
printf 'Cannot read settings.conf\n' >&2
exit 1
fi
printf 'Configuration check passed\n'
exit 0
This creates a useful contract:
- Human-readable context goes to standard output or standard error.
- The exit status tells the caller whether the operation succeeded.
- The script’s documentation explains any distinct non-zero statuses it uses.
Functions use return
A Bash function can use return to send a status back to the code
that called it. Without an explicit return, the function
normally uses the status of its last command.
config_is_readable() {
test -r "$1"
}
if config_is_readable settings.conf; then
printf 'Ready\n'
fi
A shell function’s return value is a status, not an arbitrary text value. If a function needs to produce data, it can write that data to standard output while still using its status to report success or failure.
Mini Lab: Watch the Shell Make Decisions
This lab creates a temporary directory, works only inside it, and removes it when you finish.
SA_EXIT_LAB=$(mktemp -d)
cd "$SA_EXIT_LAB" || exit 1
printf 'Workspace: %s\n' "$SA_EXIT_LAB"
printf 'server=ready\n' > app.conf
status=$?
printf 'Create status: %s\n' "$status"
You should see 0 because the redirection and command
succeeded.
test -f missing.conf
status=$?
printf 'Missing-file test status: %s\n' "$status"
The test returns non-zero because the file does not exist. That is a useful false result, not a system malfunction.
test -r app.conf && printf 'Readable\n'
test -r missing.conf || printf 'Not readable\n'
printf 'Removing: %s\n' "$SA_EXIT_LAB"
cd /tmp || exit 1
rm -r -- "$SA_EXIT_LAB"
unset SA_EXIT_LAB
Common Exit Code Mistakes
$? too late
Another command has already replaced the status. Save it immediately when
you need it.
if when a decision needs multiple actions or a clear
explanation.
Knowledge Check
1. What does exit status 0 mean to Bash?
2. Why must you inspect $? immediately?
$? may describe a later command instead of the command
you intended to check.
3. When does the right side of && run?
4. When does the right side of || run?
5. Which command normally determines a Bash pipeline’s exit status?
pipefail changes how failures earlier in the pipeline affect
the result.
6. What is the difference between function output and a function return status?
An exit code is the shell’s decision signal. A command
returns 0 for success and a non-zero status for another
outcome. Bash exposes the latest result through $? and uses it
to drive &&, ||, if,
pipelines, functions, and scripts.
Once you separate readable output from machine-readable status, shell automation becomes easier to understand: commands do the work, streams carry the information, and exit codes decide what happens next.








