Skip to main content
RunBook Academy

LinuxII · Shell and Command-Line OperationsControl flow

Exit codes, conditionals, and tests

Foundation⏱ ~10 minbashtest[[[grep

What you'll learn

  • Drive conditional execution with $?, &&, ||, and if
  • Use test, [, and [[ for string, integer, and file predicates
  • Read command output into shell variables safely
  • Distinguish success from "no matches" correctly

Prerequisites

Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-09

Not yet marked complete on this device.

The exit code is the universal decision signal in shell. This lesson shows the constructs that read it.

Conditional operators

The two shortest conditional constructs:

OperatorMeaning
&&Run the right-hand command only if the left-hand exited 0
||Run the right-hand command only if the left-hand exited non-zero
Configuration changechain on success
$ mkdir -p /tmp/example && cd /tmp/example && pwd
/tmp/example

Illustrative output

Read-only / Safechain on failure
$ test -f /etc/shadow && echo exists || echo missing
exists

Illustrative output

The if statement

if condition; then
    :   # success branch
elif other_condition; then
    :   # second branch
else
    :   # failure branch
fi

The : is bash’s null command — it does nothing and returns 0. It is here because a branch containing only a comment is a syntax error: bash requires at least one command per branch, and shellcheck reports SC1073: Couldn't parse this then clause. : is the idiomatic placeholder, and it is also how you write a deliberately empty branch in real code.

The condition is any command. Its exit code is what if reads.

Read-only / Safeif with grep
$ if grep -q '^root:' /etc/passwd; then echo found; else echo missing; fi
found

Illustrative output

Tests: [, [[, and test

Bash has three ways to write a test expression:

  • test expression — the POSIX-portable form.
  • [ expression ] — the same form, with the brackets as syntax sugar. Note the spaces inside the brackets — [ foo = bar ] is a test; [foo=bar] is something else.
  • [[ expression ]] — the bash extension. Adds pattern matching, regex matching (=~), and removes some word-splitting hazards.
Read-only / Safeinteger comparison
$ x=10; if [[ "$x" -ge 5 ]]; then echo big; else echo small; fi
big

Illustrative output

Common test operators

CategoryPOSIX [Bash [[
String equality= , !== , == , !=
Integer equality-eq, -ne, -lt, -le, -gt, -ge-eq, -ne, -lt, -le, -gt, -ge
File existence-e file-e file
File is regular-f file-f file
File is directory-d file-d file
File is readable-r file-r file
File is non-empty-s file-s file
String is non-empty-n string-n string
String is empty-z string-z string
Logical ANDexpr1 -a expr2expr1 && expr2
Logical ORexpr1 -o expr2expr1 || expr2
Negation! expr! expr
Pattern matchstring == pattern
Regex matchstring =~ regex

Reading command output

The right way to capture command output in a variable:

Read-only / Safecommand substitution
$ kernel=$(uname -r); echo "running kernel: $kernel"
running kernel: 6.6.31-linuxkit

Illustrative output

The wrong way:

Read-only / Safelegacy command substitution
$ kernel=`uname -r`; echo "running kernel: $kernel"
running kernel: 6.6.31-linuxkit

Illustrative output

The “no matches” trap

grep, find -print, test -f, and many other commands return non-zero when they find nothing. This is not an error — it is the documented way to signal “no results”. Scripts that treat it as an error need explicit handling:

Read-only / Safegrep no-match
$ if grep -q 'definitely-not-present' /etc/passwd; then echo found; else echo "exit=$? — no matches is exit 1, not an error"; fi
exit=1 — no matches is exit 1, not an error

Illustrative output

Knowledge check

Knowledge check · 3 questions

  1. Q1. Given `make && make install || echo failed`, in which scenario does the script print 'failed'?

  2. Q2. grep exits 1 when the pattern does not match any lines.

  3. Q3. Which of the following test expressions are correct POSIX `[` syntax? Select all that apply.

Passing score: 75%. Answers are checked in this browser.