Bash Cheatsheet
Control Flow
Use this Bash reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
if / elif / else
if command; then echo "command succeeded" fi if [ condition ]; then echo "true" elif [ other ]; then echo "other" else echo "false" fi # One-liner if [ -f file.txt ]; then echo "exists"; fi # Preferred modern test with [[ ]] if [[ $name == "Alice" ]]; then echo "hi Alice" fi
Test Operators — Files
| Operator | True if |
|---|---|
-e file | file exists (any type) |
-f file | file exists and is a regular file |
-d file | file exists and is a directory |
-L file | file is a symbolic link |
-r file | file is readable |
-w file | file is writable |
-x file | file is executable |
-s file | file exists and is non-empty |
-b file | block device |
-c file | character device |
-p file | named pipe (FIFO) |
-S file | socket |
f1 -nt f2 | f1 is newer than f2 |
f1 -ot f2 | f1 is older than f2 |
f1 -ef f2 | f1 and f2 are the same file (hard links) |
if [[ -f /etc/passwd ]]; then echo "file exists"; fi if [[ -d /tmp ]]; then echo "is a directory"; fi if [[ -x /usr/bin/python3 ]]; then echo "python3 executable"; fi
Test Operators — Strings
| Operator | True if |
|---|---|
-z str | string is empty (zero length) |
-n str | string is non-empty |
str1 == str2 | strings are equal (inside [[ ]]) |
str1 != str2 | strings are not equal |
str1 < str2 | str1 sorts before str2 (lexicographic) |
str1 > str2 | str1 sorts after str2 |
str =~ regex | str matches extended regex (inside [[ ]] only) |
if [[ -z "$var" ]]; then echo "empty"; fi if [[ "$a" == "$b" ]]; then echo "equal"; fi if [[ "$str" =~ ^[0-9]+$ ]]; then echo "all digits"; fi if [[ "$str" == foo* ]]; then echo "starts with foo"; fi # glob in [[ ]]
Test Operators — Integers
| Operator | Meaning |
|---|---|
-eq | equal |
-ne | not equal |
-lt | less than |
-le | less than or equal |
-gt | greater than |
-ge | greater than or equal |
if [[ $count -gt 0 ]]; then echo "non-zero"; fi if (( count > 0 )); then echo "non-zero (arithmetic)"; fi # preferred for numbers
[ ] vs [[ ]] vs (( ))
| Form | Use for | Notes |
|---|---|---|
[ ] | POSIX tests | requires quoting; = for string equality |
[[ ]] | Bash tests | no word splitting; glob/regex; ==/!=/=~ |
(( )) | Arithmetic | no $ needed; C-style operators; returns 1/0 |
$(( )) | Arithmetic value | expands to a number |
[ "$a" = "$b" ] # POSIX: note single = [[ $a == $b ]] # Bash: safer, no quotes needed for most cases (( a == b )) # arithmetic: no $ needed, 0/1 exit status
Compound Conditions
# In [[ ]] if [[ $a -gt 0 && $b -gt 0 ]]; then echo "both positive"; fi if [[ $a -gt 0 || $b -gt 0 ]]; then echo "at least one positive"; fi if [[ ! -f file ]]; then echo "no file"; fi # In [ ] — use -a and -o (or chain with && ||) if [ $a -gt 0 ] && [ $b -gt 0 ]; then echo "both"; fi # preferred if [ $a -gt 0 -a $b -gt 0 ]; then echo "both"; fi # avoid (-a is deprecated)
case Statement
case "$var" in "start") echo "starting" ;; "stop" | "halt") # multiple patterns with | echo "stopping" ;; [0-9]*) # glob pattern: starts with digit echo "number" ;; *.txt) # glob: ends in .txt echo "text file" ;; *) # default catch-all echo "unknown" ;; esac # Fall-through (Bash 4+): ;& continues to next block, ;;& tests next pattern case "$x" in a) echo "a" ;& # falls through to b b) echo "b" ;; c*) echo "starts c" ;;& # continues testing c[0-9]) echo "c+digit" ;; esac
Short-circuit Evaluation
# && is "and-then" — right side runs only if left side succeeds mkdir -p /tmp/foo && echo "created" # || is "or-else" — right side runs only if left side fails cd /nonexistent || { echo "error"; exit 1; } # Common patterns [[ -f file ]] && cat file [[ -z "$1" ]] && { echo "usage: $0 <arg>"; exit 1; } # Ternary-style result=$(( a > b ? a : b )) # arithmetic ternary
Null Coalescing Patterns
# Use default if unset name=${1:-"world"} echo "Hello, $name" # Assign default if unset : ${PORT:=8080} echo "Port: $PORT" # Error if required variable missing : ${API_KEY:?"API_KEY must be set"}