Loading
0xB0Lesson 12 of 18

Exit codes and conditions

Use exit statuses, && and ||, if with [[ ]] and (( )), and case to make decisions.

28 min 7-question quiz 2 code exercises
By the end of this lesson you can
  • Read and set exit statuses, and chain commands with && and ||
  • Write if/elif/else with [[ ]] string, number and file tests and (( )) arithmetic
  • Match patterns with case and regular expressions with =~

Every command finishes with an exit status: a number from 0 to 255. 0 means success; anything else is a kind of failure (grep uses 1 for “no match” and 2 for “error”). The last one is in $?. Your scripts set theirs with exit 0 or exit 1.

This turns the shell into a logic machine:

  • a && b - run b only if a succeeded: mkdir build && cd build
  • a || b - run b only if a failed: ping -c1 relay || echo "relay down"
  • ! a - flip success and failure
status.sh
1true;  echo "true -> $?"
2false; echo "false -> $?"
3grep -q coffee <<< "coffee machine"; echo "found -> $?"
4grep -q tea <<< "coffee machine";    echo "not found -> $?"
5ls /no/such/dir 2>/dev/null;         echo "ls error -> $?"
6mkdir -p /tmp/demo && echo "made it"
7grep -q tea <<< "coffee" || echo "no tea aboard"
Output
true -> 0
false -> 1
found -> 0
not found -> 1
ls error -> 2
made it
no tea aboard

if: run commands, branch on their status

if doesn’t test “true or false” - it runs a command and checks whether it succeeded:

1if grep -q ERROR station.log; then
2  echo "Errors found"
3elif grep -q WARN station.log; then
4  echo "Warnings only"
5else
6  echo "All clear"
7fi

For comparisons, the command is usually [[ ... ]], Bash’s test syntax (the spaces inside the brackets are required):

TestTrue when
[[ $a == $b ]] / !=strings are equal / differ (an unquoted right side is a glob pattern: [[ $f == *.log ]])
[[ -z $s ]] / [[ -n $s ]]string is empty / not empty
[[ $n -eq 5 ]]numbers: -eq -ne -lt -le -gt -ge
[[ -e $p ]] -f -dpath exists / is a file / is a directory
[[ -r $p ]] -w -x -sreadable / writable / executable / not empty
[[ $s =~ ^[0-9]+$ ]]matches a regex; groups land in BASH_REMATCH
[[ a && b ]], [[ a || b ]], [[ ! a ]]combine tests

For numbers, (( ... )) reads more naturally: if (( fuel < 20 )); then.

Try it

Succeed or fail?

Predict the exit status of each test. Remember: 0 is success.

0 of 8 sortedScore 0/0
  • “[[ 10 -lt 9 ]]”

  • “[[ 10 < 9 ]]”

  • “[[ -z "" ]]”

  • “[[ reactor.log == *.log ]]”

  • “[[ reactor.log == "*.log" ]]”

  • “(( 3 * 4 > 10 ))”

  • “[[ KS-042 =~ ^KS-[0-9]+$ ]]”

  • “! true”

checks.sh
1cd "$(mktemp -d)"
2touch manifest.txt
3mkdir cargo
4for path in manifest.txt cargo missing; do
5  if [[ -f $path ]]; then echo "$path: file"
6  elif [[ -d $path ]]; then echo "$path: directory"
7  else echo "$path: not found"
8  fi
9done
10code="KS-204"
11if [[ $code =~ ^KS-([0-9]+)$ ]]; then
12  echo "ticket number ${BASH_REMATCH[1]}"
13fi
Output
manifest.txt: file
cargo: directory
missing: not found
ticket number 204

case: many patterns, one value

case compares one value against glob patterns, top to bottom, and runs the first match. | separates alternatives, *) catches everything else, and each branch ends with ;;. It’s the natural way to handle commands and options:

case.sh
1for command in status open close self-destruct; do
2  case $command in
3    status)      echo "All systems nominal" ;;
4    open|close)  echo "Airlock: ${command}d" ;;
5    self-*)      echo "Nice try." ;;
6    *)           echo "Unknown: $command" ;;
7  esac
8done
Output
All systems nominal
Airlock: opend
Airlock: closed
Nice try.

Spotted the bug? ${command}d makes “opend”. Real scripts are full of tiny surprises like this - which is why you test them with real input.

Key takeaways

  • Exit status 0 is success; $? holds the last one; exit N sets your script’s.

  • && runs on success, || on failure.

  • if runs a command; [[ ]] tests strings, numbers, files and regexes; (( )) compares numbers.

  • case matches one value against glob patterns.

Lesson quiz

7 questions · pass with 5 correct · up to 50 XP

Passing this quiz completes the lesson and keeps your streak going. Questions you miss come back in review sessions later.

Practice: write Bash scripts

Write a script in the editor and run it for real against sample input. Each run gets a fresh Linux sandbox with Bash 5.2 and the GNU tools on Wandbox, a free public service - so experiment freely, even with rm. Your script and test input are sent there.

Exercise 1

Fuel gauge

+25 XP

stdin is a fuel reading. Print exactly one word:

  • INVALID if it isn’t a whole number from 0 to 100 (check digits with a regex first!)
  • CRITICAL below 10
  • LOW below 30
  • OK otherwise
  • 7 is critical
  • 29 is low
  • 30 is ok
  • 100 is ok
  • 140 is invalid
  • Text is invalid
script.sh
Loading editor…

Your script runs with Bash 5.2 and GNU tools on Wandbox, a free public service, in a fresh sandbox each time. Your script and test input are sent to that service.

Exercise 2

Airlock command handler

+25 XP

stdin is one command line: a command and maybe an argument. Use read -r command target and a case statement:

  • open or close → Airlock 2 opening / Airlock 2 closing (the target is the airlock number; use 1 if none is given)
  • status → All airlocks sealed
  • anything else → Unknown command: <command>
  • open 2
  • close
  • status
  • dance
script.sh
Loading editor…

Your script runs with Bash 5.2 and GNU tools on Wandbox, a free public service, in a fresh sandbox each time. Your script and test input are sent to that service.

Questions about this lesson

Stuck? Ask. Figured something out? Share it. Explaining is one of the best ways to learn.

Loading posts…

Did you like the lesson? 😆👍
Consider a donation to support our work: