Files
nvim/cheatsheets/bash.md
T
2026-06-16 16:26:28 +01:00

30 KiB

Bash Cheat Sheet

Conditionals

if [[ -z "$string" ]]; then
    echo "String is empty"
elif [[ -n "$string" ]]; then
    echo "String is not empty"
fi

String conditions

┌────────────────────────┬──────────────────┐ │ [[ -z STRING ]] │ Empty string │ ├────────────────────────┼──────────────────┤ │ [[ -n STRING ]] │ Not empty string │ ├────────────────────────┼──────────────────┤ │ [[ STRING == STRING ]] │ Equal │ ├────────────────────────┼──────────────────┤ │ [[ STRING != STRING ]] │ Not Equal │ ├────────────────────────┼──────────────────┤ │ [[ STRING =~ STRING ]] │ Regex │ └────────────────────────┴──────────────────┘

Number conditions

┌────────────────────────┬───────────────────────┐ │ [[ NUM1 -eq NUM2 ]] │ Equal │ ├────────────────────────┼───────────────────────┤ │ [[ NUM1 -ne NUM2 ]] │ Not Equal │ ├────────────────────────┼───────────────────────┤ │ [[ NUM1 -lt NUM2 ]] │ Less than │ ├────────────────────────┼───────────────────────┤ │ [[ NUM1 -le NUM2 ]] │ Less than or equal │ ├────────────────────────┼───────────────────────┤ │ [[ NUM1 -gt NUM2 ]] │ Greater than │ ├────────────────────────┼───────────────────────┤ │ [[ NUM1 -ge NUM2 ]] │ Greater than or equal │ ├────────────────────────┼───────────────────────┤ │ (( NUM < NUM )) │ Numeric conditions │ └────────────────────────┴───────────────────────┘

File conditions

┌───────────────────────┬─────────────────────────┐ │ [[ -e FILE ]] │ Exists │ ├───────────────────────┼─────────────────────────┤ │ [[ -r FILE ]] │ Readable │ ├───────────────────────┼─────────────────────────┤ │ [[ -h FILE ]] │ Symlink │ ├───────────────────────┼─────────────────────────┤ │ [[ -d FILE ]] │ Directory │ ├───────────────────────┼─────────────────────────┤ │ [[ -w FILE ]] │ Writable │ ├───────────────────────┼─────────────────────────┤ │ [[ -s FILE ]] │ Size is > 0 bytes │ ├───────────────────────┼─────────────────────────┤ │ [[ -f FILE ]] │ File │ ├───────────────────────┼─────────────────────────┤ │ [[ -x FILE ]] │ Executable │ ├───────────────────────┼─────────────────────────┤ │ [[ FILE1 -nt FILE2 ]] │ 1 is more recent than 2 │ ├───────────────────────┼─────────────────────────┤ │ [[ FILE1 -ot FILE2 ]] │ 2 is more recent than 1 │ ├───────────────────────┼─────────────────────────┤ │ [[ FILE1 -ef FILE2 ]] │ Same files │ └───────────────────────┴─────────────────────────┘

Other conditions

┌────────────────────┬────────────────────┐ │ [[ -o noclobber ]] │ Option enabled │ ├────────────────────┼────────────────────┤ │ [[ ! EXPR ]] │ Not │ ├────────────────────┼────────────────────┤ │ [[ X && Y ]] │ And │ ├────────────────────┼────────────────────┤ │ [[ X || Y ]] │ Or │ └────────────────────┴────────────────────┘

Parameter manipulation

┌─────────────────────────────────┬────────────────────────────────────────────────────────────────┐ │ ${foo} │ Variable │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${foo/from/to} │ Replace first match │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${foo/#from/to}/${foo/%from/to} │ Replace first match anchored to start (default) or end │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${foo//from/to} │ Replace all │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${foo:start:length} │ Slice from start to length (both optional and can be negative) │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${foo%suffix}/${foo%%suffix} │ Remove suffix (ungreedy/greedy) │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${foo#prefix}/${foo##prefix} │ Remove prefix (ungreedy/greedy) │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${#foo} │ Length of foo │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${foo,} │ Lowercase first letter │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${foo,,} │ Lowercase all letters │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${foo^} │ Uppercase first letter │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${foo^^} │ Uppercase all letters │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${foo:-val} │ Use val if foo is unset or null │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${foo:=val} │ Set foo to val if unset or null │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${foo:+val} │ Use val if foo is set and not null │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${foo:?message} │ Print message and exit if foo is unset or null │ └─────────────────────────────────┴────────────────────────────────────────────────────────────────┘

Omitting the : for default values will only check if the variable is set, not if it is null.

Examples

name="John"
echo "${name}"
echo "${name/J/j}"    #=> "john" (substitution)
echo "${name:0:2}"    #=> "Jo" (slicing)
echo "${name::2}"     #=> "Jo" (slicing)
echo "${name::-1}"    #=> "Joh" (slicing)
echo "${name:(-1)}"   #=> "n" (slicing from right)
echo "${name:(-2):1}" #=> "h" (slicing from right)
echo "${food:-Cake}"  #=> $food or "Cake"

length=2
echo "${name:0:length}" #=> "Jo" (slicing with variable)

str="/path/to/foo.cpp"
echo "${str%.cpp}"    # /path/to/foo
echo "${str%.cpp}.o"  # /path/to/foo.o
echo "${str%/*}"      # /path/to

echo "${str##*.}"     # cpp (extension)
echo "${str##*/}"     # foo.cpp (basepath)
echo "${str#*o}"      # o/foo.cpp (first occurrence of 'o')
echo "${str##*o}"     # oo.cpp (last occurrence of 'o')

echo "${str#*/}"      # path/to/foo.cpp
echo "${str##*/}"     # foo.cpp

echo "${str/foo/bar}" # /path/to/bar.cpp

str="HELLO WORLD!"
echo "${str,}"   #=> "hELLO WORLD!" (lowercase 1st letter)
echo "${str,,}"  #=> "hello world!" (all lowercase)

str="hello world!"
echo "${str^}"   #=> "Hello world!" (uppercase 1st letter)
echo "${str^^}"  #=> "HELLO WORLD!" (all uppercase)

Comments

# This is a comment

: '
This is a multi-line comment workaround
'

Loops

Basic for loop

for i in /etc/rc.*; do
    echo "$i"
done

C-style for loop

for (( i = 0; i < 10; i++ )); do
    echo "$i"
done

Ranges

for i in {1..10}; do
    echo "$i"
done

# With step size
for i in {1..10..2}; do
    echo "$i"
done

Reading lines

while IFS= read -r line; do
    echo "$line"
done < file.txt

Infinite loop

while true; do
    echo "Press [CTRL+C] to stop.."
    sleep 1
done

History

┌───────────────┬────────────────────────────────────────────────────────────────┐ │ !! │ Expand latest command in history │ ├───────────────┼────────────────────────────────────────────────────────────────┤ │ !$ │ Expand last parameter of latest command │ ├───────────────┼────────────────────────────────────────────────────────────────┤ │ !* │ Expand all parameters of latest command │ ├───────────────┼────────────────────────────────────────────────────────────────┤ │ !-n │ Expand nth latest command in history │ ├───────────────┼────────────────────────────────────────────────────────────────┤ │ !n │ Expand nth command in history │ ├───────────────┼────────────────────────────────────────────────────────────────┤ │ !string │ Expand latest command starting with string │ ├───────────────┼────────────────────────────────────────────────────────────────┤ │ !!:s/from/to │ Replace first occurrence of "from" with "to" in latest command │ ├───────────────┼────────────────────────────────────────────────────────────────┤ │ !!:gs/from/to │ Replace all occurrences of "from" with "to" in latest command │ ├───────────────┼────────────────────────────────────────────────────────────────┤ │ !!:p │ Print latest command without executing it │ ├───────────────┼────────────────────────────────────────────────────────────────┤ │ !$:t │ Expand basename of last parameter of latest command │ ├───────────────┼────────────────────────────────────────────────────────────────┤ │ !$:h │ Expand directory of last parameter of latest command │ ├───────────────┼────────────────────────────────────────────────────────────────┤ │ !!:n │ Expand only nth parameter of latest command │ ├───────────────┼────────────────────────────────────────────────────────────────┤ │ !^ │ Expand first parameter of latest command │ ├───────────────┼────────────────────────────────────────────────────────────────┤ │ !$ │ Expand last parameter of latest command │ ├───────────────┼────────────────────────────────────────────────────────────────┤ │ !!:n-m │ Expand parameters from nth to mth of latest command │ ├───────────────┼────────────────────────────────────────────────────────────────┤ │ !!:n-$ │ Expand parameters from nth to last of latest command │ └───────────────┴────────────────────────────────────────────────────────────────┘

Redirection

┌────────────────────────────┬─────────────────────────────────┐ │ SCRIPT > FILE │ stdout to FILE │ ├────────────────────────────┼─────────────────────────────────┤ │ SCRIPT >> FILE │ Append stdout to FILE │ ├────────────────────────────┼─────────────────────────────────┤ │ SCRIPT 2> FILE │ stderr to FILE │ ├────────────────────────────┼─────────────────────────────────┤ │ SCRIPT 2>&1/SCRIPT &> FILE │ stderr to stdout │ ├────────────────────────────┼─────────────────────────────────┤ │ SCRIPT 2> /dev/null │ stderr to /dev/null │ ├────────────────────────────┼─────────────────────────────────┤ │ SCRIPT > FILE 2>&1 │ stdout and stderr to FILE │ ├────────────────────────────┼─────────────────────────────────┤ │ SCRIPT &> /dev/null │ stdout and stderr to /dev/null │ ├────────────────────────────┼─────────────────────────────────┤ │ echo "$0: warning" >&2 │ Print to stderr │ ├────────────────────────────┼─────────────────────────────────┤ │ SCRIPT < FILE │ stdin from FILE │ └────────────────────────────┴─────────────────────────────────┘

Switch statement

case "$1" in
    start | up)
        vagrant up
        ;;

    *)
        echo "Usage: $0 {start|stop|ssh}"
        ;;
esac

Options switch statement

while [[ "$1" =~ ^- && ! "$1" == "--" ]]; do case $1 in
    -V | --version )
        echo "$version"
        exit
        ;;
    -s | --string )
        shift; string=$1
        ;;
    -f | --flag )
        flag=1
        ;;
esac; shift; done
if [[ "$1" == '--' ]]; then shift; fi

Functions

my_function() {
    echo "Hello, World!"
}

Arguments

┌────┬───────────────────────────────────┐ │ $# │ Number of arguments │ ├────┼───────────────────────────────────┤ │ $* │ All arguments │ ├────┼───────────────────────────────────┤ │ $@ │ All arguments as separate strings │ ├────┼───────────────────────────────────┤ │ $1 │ First argument │ ├────┼───────────────────────────────────┤ │ $_ │ Last argument │ └────┴───────────────────────────────────┘

Arrays

# Create an array in one line
Fruits=('Apple' 'Banana' 'Orange')

# Or assign each value
Fruits[0]="Apple"
Fruits[1]="Banana"
Fruits[2]="Orange"

# Associative array
declare -A sounds

sounds[dog]="bark"
sounds[cow]="moo"
sounds[bird]="tweet"
sounds[wolf]="howl"

Array element expansion

┌──────────────────────┬────────────────────────────────────────────────────┐ │ ${array[key]} │ Element at key │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ${array[-1]} │ Last element (not for associative) │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ${array[@]} │ All elements │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ${#array[@]} │ Number of elements │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ${#array} │ String length of 1st element (not for associative) │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ${#array[key]} │ String length of element at key │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ${array[@]:n:length}length eements from position n │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ${!array[@]} │ Keys of all elements │ └──────────────────────┴────────────────────────────────────────────────────┘

Operations

Fruits=("${Fruits[@]}" "Watermelon")    # Push
Fruits+=('Watermelon')                  # Also Push
Fruits=( "${Fruits[@]/Ap*/}" )          # Remove by regex match
unset Fruits[2]                         # Remove one item
Fruits=("${Fruits[@]}")                 # Duplicate
Fruits=("${Fruits[@]}" "${Veggies[@]}") # Concatenate
words=($(< datafile))                   # From file (split by IFS)

Looping

for i in "${Fruits[@]}"; do
  echo "$i"
done

for key in "${!sounds[@]}"; do
  echo "$key"
done

Misc

Directory of script

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"

Special variables

┌──────────────────┬────────────────────────────────────────┐ │ $? │ Exit status of last task │ ├──────────────────┼────────────────────────────────────────┤ │ $! │ PID of last background task │ ├──────────────────┼────────────────────────────────────────┤ │ $$ │ PID of shell │ ├──────────────────┼────────────────────────────────────────┤ │ $0 │ Filename of the shell script │ ├──────────────────┼────────────────────────────────────────┤ │ $_ │ Last arugment of altest command │ ├──────────────────┼────────────────────────────────────────┤ │ ${PIPESTATUS[n]} │ Return value of piped commands (array) │ └──────────────────┴────────────────────────────────────────┘