Bash Exit Codes

Every command ends with a small number called an exit code. The number tells the shell whether the command worked. Scripts read exit codes to decide what to do next, such as retrying a job or stopping with a warning.

The Report Card Analogy

A student returns from an exam with a score. Zero errors means a perfect paper. Any other number points to a problem. Commands report their results the same way: 0 means success, and any other value means failure.

   Command runs
        |
        v
   +-----------+
   | Exit code |
   +-----+-----+
         |
    +----+-----+
    |          |
    v          v
    0       1 to 255
  Success    Failure

Read the Last Exit Code

The special variable $? holds the exit code of the most recent command.

ls /home
echo $?
ls /missing_folder
echo $?

Output:

asha
0
ls: cannot access '/missing_folder': No such file or directory
2

The value changes after every command, so store it in a variable when you need it later.

grep "error" app.log
status=$?
echo "Search finished with code $status"

Common Exit Codes

CodeMeaning
0Success
1General failure
2Wrong usage of a command
126File found but not executable
127Command not found
130Stopped with Ctrl+C
137Stopped by a forced kill signal

Codes above 128 mean the program stopped because of a signal. The signal number equals the code minus 128.

Set an Exit Code in Your Script

The exit command ends a script and sets its code.

#!/bin/bash
if [ ! -f "$1" ]; then
  echo "File not found: $1" >&2
  exit 1
fi

echo "File exists"
exit 0

Pick clear numbers for different problems and document them in comments:

# 0 = success
# 1 = missing argument
# 2 = file not found
# 3 = no permission to read

Return Codes From Functions

A function uses return to report its result. The caller reads the value with $?.

#!/bin/bash
is_even() {
  if (( $1 % 2 == 0 )); then
    return 0
  else
    return 1
  fi
}

if is_even 8; then
  echo "8 is even"
fi
if ! is_even 7; then
  echo "7 is odd"
fi

The return command carries numbers from 0 to 255. Use echo to pass back text.

Use Exit Codes in Conditions

The if statement checks the exit code directly:

if grep -q "Asha" team.txt; then
  echo "Found"
else
  echo "Not found"
fi

Chain Commands With AND and OR

mkdir reports && cd reports          # run second only if first succeeds
cd backup || echo "Folder missing"     # run second only if first fails
make_backup && echo "Done" || echo "Failed"

The double ampersand means "and then, if it worked". The double pipe means "otherwise".

Exit Codes in Pipelines

A pipeline reports only the code of its last command by default. The pipefail option reports the first failure instead:

set -o pipefail
cat missing.txt | sort
echo $?

The array ${PIPESTATUS[@]} holds the code of every step in the most recent pipeline.

Stop on the First Failure

#!/bin/bash
set -e
echo "Step 1"
false
echo "Step 2"

The script prints Step 1 and stops at false, which always fails. Line three never runs.

Practical Example: Retry a Command

#!/bin/bash
attempts=3
count=1

until ping -c 1 example.com > /dev/null 2>&1
do
  echo "Attempt $count failed"
  if [ $count -ge $attempts ]; then
    echo "Giving up"
    exit 1
  fi
  count=$((count + 1))
  sleep 2
done

echo "Network is reachable"

Key Takeaways

  • Exit code 0 means success and any other value means failure.
  • $? holds the code of the last command.
  • exit N ends a script with code N.
  • && and || chain commands based on success or failure.
  • set -e and pipefail make failures stop a script early.