Files
2026-06-16 16:26:28 +01:00

36 KiB

ZSH 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 (length can be negative) │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${foo%suffix}/${foo%%suffix} │ Remove suffix (ungreedy/greedy) │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${foo#prefix}/${foo##prefix} │ Remove prefix (ungreedy/greedy) │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${#foo} │ Length of foo │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${(L)foo} │ Lowercase all letters │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${(U)foo} │ Uppercase all letters │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${(q)foo} │ Quote safely │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${(qq)foo} │ Quote more explicitely │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${(s.del.)foo} │ Split on del │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${(j.del.)foo} │ Join with del │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${file:h} │ Directory of file │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${file:t} │ Filename of file │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${file:e} │ Extension of file │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${file:r} │ Root of file (without extension) │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${file:a} │ Absolute path of file │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${file:A} │ Absolute path of file (resolves symlinks) │ ├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤ │ ${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 "${food:-Cake}"  #=> $food or "Cake"

length=2
echo "${name[1,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 "${(L)str}"  #=> "hello world!" (all lowercase)

str="hello world!"
echo "${(U)str}"  #=> "HELLO WORLD!" (all uppercase)

Globs

┌───────────────┬───────────────────────┐ │ ls **/*.js │ Recursive expansion │ ├───────────────┼───────────────────────┤ │ ls *(.) │ Plain files │ ├───────────────┼───────────────────────┤ │ ls *(/) │ Directories │ ├───────────────┼───────────────────────┤ │ ls *(@) │ Symlinks │ ├───────────────┼───────────────────────┤ │ ls *(*) │ Executables │ ├───────────────┼───────────────────────┤ │ ls *(L+1M) │ Files larger than 1MB │ ├───────────────┼───────────────────────┤ │ ls *(om[1,5]) │ 5 newest files │ ├───────────────┼───────────────────────┤ │ ls *(.)~*.bak │ Files except *.bak │ └───────────────┴───────────────────────┘

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
print -r -- "$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 │ stderr to stdout │ ├────────────────────────┼─────────────────────────────────┤ │ SCRIPT &> FILE │ stderr and stdout to FILE │ ├────────────────────────┼─────────────────────────────────┤ │ 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[1]="Apple"
Fruits[2]="Banana"
Fruits[3]="Orange"

# Associative array
typeset -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[key]} │ String length of element at key │ ├──────────────────────────────────────┼────────────────────────────────────┤ │ ${array:n:length}/${array[n,length]}length eements from position n │ ├──────────────────────────────────────┼────────────────────────────────────┤ │ ${(k)array} │ Keys of all elements │ └──────────────────────────────────────┴────────────────────────────────────┘

Operations

Fruits=("${Fruits[@]}" "Watermelon")    # Push
Fruits+=('Watermelon')                  # Also Push
Fruits=( "${Fruits[@]/Ap*/}" )          # Remove by regex match
unset Fruits[3]                         # 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 val in "${(v)sounds}"; do
  echo "$val"
done

for key in "${(k)sounds}"; do
  echo "$key"
done

Misc

Directory of script

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

Special variables

┌──────────────────┬───────────────────────────────────────────────┐ │ $? │ Exit status of last task │ ├──────────────────┼───────────────────────────────────────────────┤ │ $! │ PID of last background task │ ├──────────────────┼───────────────────────────────────────────────┤ │ $$ │ PID of shell │ ├──────────────────┼───────────────────────────────────────────────┤ │ $# │ Number of arguments passed to script │ ├──────────────────┼───────────────────────────────────────────────┤ │ $0 │ Filename of the shell script │ ├──────────────────┼───────────────────────────────────────────────┤ │ $_ │ Last arugment of altest command │ ├──────────────────┼───────────────────────────────────────────────┤ │ $argv │ Array of all arguments passed to script │ ├──────────────────┼───────────────────────────────────────────────┤ │ $ARGC │ Number of arguments passed to script │ ├──────────────────┼───────────────────────────────────────────────┤ │ ${pipestatus[n]} │ Return value of piped commands (array) │ ├──────────────────┼───────────────────────────────────────────────┤ │ $RANDOM │ Random number between 0 and 32767 │ ├──────────────────┼───────────────────────────────────────────────┤ │ $SECONDS │ Number of seconds since the shell was started │ ├──────────────────┼───────────────────────────────────────────────┤ │ $PWD │ Current working directory │ ├──────────────────┼───────────────────────────────────────────────┤ │ $OLDPWD │ Previous working directory │ └──────────────────┴───────────────────────────────────────────────┘