Control flow — if, for, while, case
Branch on exit codes, loop over lists or streams, match patterns. Bash control flow is quirky — exit code 0 means true.
if checks exit codes, not booleans. if cmd; then ...; fi runs then if cmd exits 0. Counter-intuitive: exit 0 = success = 'true'; any non-zero = false. if grep -q foo file; then echo found; fi.
Tests: [[ expr ]] (Bash's modern test) and [ expr ] (POSIX). Examples: [[ -f file ]] (file exists), [[ -d dir ]] (directory exists), [[ -z $s ]] (empty string), [[ $a == $b ]] (string equal), [[ $n -lt 10 ]] (integer less than).
Loops: for x in a b c; do echo $x; done. Over files: for f in *.log; do ...; done. Numeric: for i in {1..5}; do ...; done. C-style: for ((i=0; i<5; i++)); do ...; done. While: while cmd; do ...; done.
case for multi-way branches: cleaner than chained if/elif. Uses glob patterns, not regex. Example: case "$env" in prod) ... ;; staging|dev) ... ;; *) default ;; esac. The ;; terminates each branch.
&& and || shorthand: cmd1 && cmd2 runs cmd2 only if cmd1 succeeds. cmd1 || cmd2 runs cmd2 only if cmd1 fails. mkdir -p dir && cd dir — idiomatic for 'create and enter'. cmd || echo failed for quick error messages.
Grounded on https://www.gnu.org/software/bash/manual/bash.html#Conditional-Constructs
Next up
Functions & arguments
Group commands into reusable units, accept positional parameters, and return exit codes. Not quite like functions in 'real' languages.