The shape
if [[ condition ]]; then
# ...
elif [[ other ]]; then
# ...
else
# ...
fiif takes a command. It runs the command and branches on exit code (0 = then, non-zero = else). [[ ... ]] is the bash/zsh test command — preferred over [ ... ] (POSIX) because it's safer with empty variables and supports more operators.
String tests
[[ -z "$x" ]]— string is empty.[[ -n "$x" ]]— string is non-empty.[[ "$a" == "$b" ]]— equal.[[ "$a" != "$b" ]]— not equal.[[ "$x" == prefix* ]]— glob match (only inside [[ ]]).[[ "$x" =~ ^[0-9]+$ ]]— regex match (bash/zsh).
Numeric tests
[[ $a -eq $b ]]— equal (eq, ne, lt, le, gt, ge).(( a == b ))— arithmetic context (cleaner for math).
File tests
[[ -f path ]]— regular file exists.[[ -d path ]]— directory.[[ -e path ]]— exists (any type).[[ -r path ]],-w,-x— readable / writable / executable.[[ -s path ]]— exists and non-empty.
And / or
if [[ -f config.yaml && -r config.yaml ]]; then ...
if [[ -z "$NAME" || -z "$EMAIL" ]]; then ...Inside [[ ]], use && and ||. Outside it, those operators chain whole commands.