Conditionals let a script choose. Bash has one if-statement with one quirky detail — the condition is a command, not an expression — and once that clicks, test, [ ], and [[ ]] fall into place.
The shape #
if [[ -f "$config" ]]; then
echo "using $config"
else
echo "no config found" >&2
exit 1
fi
if runs the command between it and then; if the command exits 0, the then-branch runs. That is why [ ... ] and [[ ... ]] are commands that test things and exit accordingly — and why if grep -q error log; then works with no brackets at all.
test, [ ], and [[ ]] #
test and [ are the same command (the POSIX classic); [[ is the bash keyword with better behaviour: no word-splitting surprises inside, pattern matching, and && / || between tests.
| Test | True when |
|---|---|
-f file / -d dir | File / directory exists |
-r / -w / -x | Readable / writable / executable |
-z s / -n s | String empty / non-empty |
s1 = s2 | Strings equal |
n -eq -lt -gt -ge -le | Numeric comparisons |
$s == *.log ([[ ]] only) | Pattern match |
The number one rule survives every dialect: quote the variables. Unquoted, an empty $var vanishes and [ -f $var ] becomes [ -f ] — a true statement about the two-character string "-f". Quoted, it stays a filename test. See quoting for why.
Combining and nesting #
if [[ -r "$f" && -s "$f" ]]; then
echo "$f is readable and non-empty"
elif [[ -d "$f" ]]; then
echo "$f is a directory"
else
echo "$f is neither"
fi
For dispatching on values rather than conditions — subcommands like start and stop — case reads far better than an elif ladder.
When if is the wrong tool #
Checking whether a command worked does not need an if at all — && and || chain on success and failure, which is the backbone of exit-code discipline:
mkdir -p out && cp app.conf out/
Failures deserve strict mode so unhandled ones stop the script; exit codes make the choices observable. The scripting series continues.
