Exit Codes: Using $? and && ||
What You'll Learn
- The idea of an exit status (exit code) that signals success or failure
- How to check the result of the previous command with
$? - How to branch on "if it succeeded" and "if it failed" with
&&and|| - A first step toward handling errors properly in shell scripts
Quick Summary
- Every command returns a number when it finishes (
0means success, anything else means failure) - Want to see the last result?
echo $? - Run next on success →
&& - Run next on failure →
||
1. What Is an Exit Status?
Conclusion: An exit status is the number a command returns.
0means success;1to255mean failure.
0 means success, anything other than 0 means failure. That single number tells you whether the command worked.The basic rule
| Number | Meaning |
|---|---|
0 |
Success |
1–255 |
Failure (some problem) |
"0 means success" is counterintuitive, so lock it in first.
2. Why Does the Exit Status Matter?
Conclusion: The exit status is what lets you automate "run the next step only when this one succeeded."
Judging success only by what's printed on screen is risky. A command can fail even when no "Error" text appears. Relying on the machine-readable exit status is far more reliable.
3. Check the Result with $?
Conclusion:
$?holds the exit status of the last command. Useecho $?to see it.
The exit status of the command you just ran is stored in the special variable $?.
$ ls /etc
hosts passwd ...
$ echo $?
0
0! So ls succeeded.ls on a path that doesn't exist.$ ls /not-exist
ls: cannot access '/not-exist': No such file or directory
$ echo $?
2
2. A different number from before!0. ls returns 2 for a "file not found" error. You don't need to memorize the exact numbers — just whether it's 0 or not.$? only holds the result of the immediately preceding command. Running echo $? twice in a row makes the second one show "the exit status of the first echo (which succeeded, so 0)." Check it only once.
4. Chain "on success" with &&
Conclusion:
cmd1 && cmd2runs cmd2 only when cmd1 succeeds (returns0).
&& means "if the left side succeeds, run the right side too."
$ mkdir backup && cp data.txt backup/
cp only if mkdir succeeded"?mkdir backup fails (say, you lack permission to create it), cp won't run at all. Use it when a step depends on the previous one succeeding.# Run tests only if the build succeeds $ make && make test # List the contents only if the cd succeeds $ cd /var/log && ls
Think of && as "proceed once the precondition is met." It safely chains steps where you want to stop if something fails.
5. Chain "on failure" with ||
Conclusion:
cmd1 || cmd2runs cmd2 only when cmd1 fails. Use it for error handling.
|| is the opposite of &&: it means "if the left side fails, run the right side."
$ cd /var/log || echo "Could not move into the directory"
cd fails it prints a message. And if it succeeds, no message?cd succeeds, the echo never runs. || is often used as a safety net for when things go wrong.# Prompt to install if a command is missing $ which jq || echo "jq is not installed. Please install it." # Exit the script if the step fails $ cp data.txt backup/ || exit 1
&& vs || at a glance
cmd1 && cmd2 → cmd2 runs if cmd1 [succeeds] cmd1 || cmd2 → cmd2 runs if cmd1 [fails]
Remember: "&& proceeds on success," "|| proceeds on failure."
6. Combining && and ||
Conclusion:
cmd && on-success || on-failurewrites "do A on success, B on failure" in one line.
Chaining && and || lets you act differently on success vs. failure.
$ ping -c1 example.com > /dev/null && echo "Connection OK" || echo "Connection NG"
echo Connection OK) itself fails, the failure action runs too. It's fine for simple messages, but for anything complex an if statement is safer.A && B || C is not strictly identical to "if A then B else C." If B fails, C also runs. When you need reliable branching, use an if statement (see the related article).
7. Common Beginner Pitfalls
Conclusion: The easy mistakes are getting "0 means success" backwards and reading
$?too late.
7-1. Remembering "0 means success" backwards
7-2. Reading $? too late
$ ls /not-exist $ pwd # ← an extra command slipped in $ echo $? # this shows pwd's result (0, success)
Check $? right after the command you care about. Any command in between overwrites $?.
7-3. Returning an exit status yourself
When you want a script or command to report success or failure to its caller, use exit.
exit 0 # end as success exit 1 # end as failure
If you call exit without a number, it returns the exit status of the last command as-is.
8. Hands-On Exercises
Conclusion: Three exercises — check, run-on-success, run-on-failure — to practice
$?,&&, and||.
Exercise 1: Run any command (e.g., ls), then print its exit status right after.
Show hint
The last result lives in $?.
Sample answer
$ ls $ echo $?
Exercise 2: Print "Created" only when mkdir test-dir succeeds.
Show hint
"Run next on success" is &&.
Sample answer
$ mkdir test-dir && echo "Created"
Exercise 3: Try to cd into a directory that doesn't exist, and print "Cannot move" if it fails.
Show hint
"Run next on failure" is ||.
Sample answer
$ cd /not-exist || echo "Cannot move"
9. Copy-Paste Templates
Conclusion: Keep the common patterns — check, on-success, on-failure, branch, and exit — within reach.
Common patterns to keep handy
# Check the last exit status echo $? # Run next only on success commandA && commandB # Run next only on failure commandA || commandB # A on success, B on failure (simple branch) command && echo "OK" || echo "NG" # Exit the script if the step fails command || exit 1 # End a script explicitly as success / failure exit 0 # success exit 1 # failure