Exit statuses are how shell scripts make decisions and detect errors.
When a command finishes it returns an exit status, an integer 0 to 255. Zero means success, anything else means failure, and programs document what their non-zero codes mean: grep returns 1 for "no match" and 2 for a real error. The status of the last command is in $?; read it immediately, because the next command overwrites it.
Bash lets you chain on that status:
a && brunsbonly ifasucceeded,a || brunsbonly ifafailed,! ainverts the status.
So mkdir -p out && cd out changes directory only when the directory exists, and command -v jq || echo "please install jq" is a one-line dependency check. You can combine them, but a && b || c is not a true if/else: c also runs when b fails.
Your own scripts end with the status of the last command run, or the number passed to exit. Use exit 0 for success and a small non-zero number (often 1, or 2 for usage errors) for failure so callers, cron and CI systems can react. true and false are commands that simply return 0 and 1.
Example
grep -q root /etc/passwd
echo $? # 0 (found)
grep -q nosuchuser /etc/passwd
echo $? # 1 (no match)
ls /nope 2>/dev/null; echo $? # 2 (error)
mkdir -p build && echo "created" # created
false || echo "fallback ran" # fallback ran
! true; echo $? # 1 (! inverts)
true; false; echo $? # 1 (only the last command counts)
command -v git >/dev/null && echo "git is installed"Remember
- 0 = success, 1-255 = failure; $? holds the last status
- && runs on success, || on failure, ! inverts
- End scripts with a meaningful exit code
Watch out for
- Using
a && b || cas if/else: c also runs when b fails.