Bash Cheat Sheet Quick Reference

A concise guide featuring essential Bash commands and syntax for beginners and advanced users alike, helping you efficiently navigate and manage tasks in the terminal.

Getting Started

greet.sh

#!/bin/bash

GREETING="universe"
echo "Greetings, $GREETING!" # => Greetings, universe!
Run the script:

$ bash greet.sh

Variables

USER="Alice"

echo ${USER}    # => Alice (Variables)
echo $USER      # => Alice (Variables)
echo "$USER"    # => Alice (Variables)
echo '$USER'    # => $USER (Exact string)
echo "${USER}!" # => Alice! (Variables)

USER = "Alice"  # => Error (about space)

Comments

# This is a single-line comment in Bash.

: '
This is a
useful multi-line comment
in Bash
'
To create multi-line comments, use :' to start and ' to end.

Arguments

$1 … $9    Argument 1 ... 9
$0         The script's name
$1         The first command-line argument
${10}     The tenth positional parameter
$#         Total count of arguments
$$         The current shell's process ID
$*         All provided arguments
$@       All arguments, starting from the first one
$-         Current shell options
$_         The last argument used in the previous command

See: Special parameters

Functions

retrieve_name() {
    echo "Alice"
}

echo "Your name is $(retrieve_name)"

Conditionals

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

Brace expansion

echo {X,Y}.txt
{X,Y}        Expands to X Y
{X,Y}.txt   Expands to X.txt Y.txt
{2..6}        Expands to 2 3 4 5 6

See: Brace expansion

Shell execution

# => I’m located at /current/path
echo "I’m located at $(PWD)"

# Equivalent to:
echo "I’m located at `pwd`"

See: Command substitution

Bash Parameter expansions

Syntax

${BAR%suffix}         Remove the specified suffix
${BAR#prefix}        Remove the specified prefix
${BAR%%suffix}       Remove the longest matching suffix
${BAR##prefix}      Remove the longest matching prefix
${BAR/from/to}       Replace the first occurrence of "from" with "to"
${BAR//from/to}      Replace all occurrences of "from" with "to"
${BAR/%from/to}      Replace the suffix "from" with "to"
${BAR/#from/to}      Replace the prefix "from" with "to"

# Substrings
${BAR:0:3}           Extract substring (start position, length)
${BAR:(-3):3}        Extract substring from the right

# Length
${#BAR}              Get the length of $BAR

# Default values
${BAR:-val}          Use $BAR, or "val" if $BAR is unset
${BAR:=val}          Set $BAR to "val" if it's unset
${BAR:+val}         Return "val" if $BAR is set
${BAR:?message}      Display "message" and exit if $BAR is unset

Substitution

FILE="/directory/to/example.txt"
echo ${FILE%.txt}    # /directory/to/example
echo ${FILE%.txt}.bak  # /directory/to/example.bak
echo ${FILE%/*}      # /directory/to

echo ${FILE##*.}     # txt (file extension)
echo ${FILE##*/}     # example.txt (filename)

echo ${FILE#*/}      # directory/to/example.txt
echo ${FILE##*/}     # example.txt

echo ${FILE/example/sample} # /directory/to/sample.txt

Slicing

person="Alice"
echo ${person}            # => Alice
echo ${person:0:3}        # => Ali
echo ${person::3}         # => Ali
echo ${person::-1}        # => Alic
echo ${person:(-1)}       # => e
echo ${person:(-2)}       # => ce
echo ${person:(-2):2}     # => ce

length=3
echo ${person:0:length}   # => Ali

See: Parameter expansion

basepath & dirpath

SOURCE="/directory/to/example.txt"

BASENAME=${SOURCE##*/}
echo $BASENAME  # => "example.txt"

DIRNAME=${SOURCE%$BASENAME}
echo $DIRNAME    # => "/directory/to/"

Transform

TEXT="GOODBYE EARTH!"
echo ${TEXT,}   # => gOODBYE EARTH!
echo ${TEXT,,}  # => goodbye earth!

TEXT="goodbye earth!"
echo ${TEXT^}   # => Goodbye earth!
echo ${TEXT^^}  # => GOODBYE EARTH!

ARRAY=(goodbye Earth)
echo "${ARRAY[@],}" # => goodbye earth
echo "${ARRAY[@]^}" # => Goodbye Earth

Bash Arrays

Defining arrays

Fruits=('Grapes' 'Mango' 'Pineapple')

Fruits[0]="Grapes"
Fruits[1]="Mango"
Fruits[2]="Pineapple"

ARRAY1=(bar{1..3}) # => bar1 bar2 bar3
ARRAY2=({X..Z})     # => X Y Z

# Merge => bar1 bar2 bar3 X Y Z
ARRAY3=(${ARRAY1[@]} ${ARRAY2[@]})

# Declare construct
declare -a Numbers=(6 7 8)
Numbers+=(9 10) # Append => 6 7 8 9 10

Indexing

${Fruits[0]}         # First element
${Fruits[-1]}        # Last element
${Fruits[*]}         # All elements (as a single string)
${Fruits[@]}         # All elements (as an array)
${#Fruits[@]}        # Total number of elements
${#Fruits}           # Length of the first element
${#Fruits[2]}       # Length of the third element
${Fruits[@]:1:2}     # Subarray (from index 1, length 2)
${!Fruits[@]}        # Indices of all elements

Iteration

Fruits=('Grapes' 'Mango' 'Pineapple')

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

# With index
for index in "${!Fruits[@]}"; do
    printf "%st%sn" "$index" "${Fruits[$index]}"
done

Operations

Fruits=("${Fruits[@]}" "Kiwi")           # Add item
Fruits+=('Kiwi')                         # Also add item
Fruits=( ${Fruits[@]/Ban*/} )             # Remove by pattern match
unset Fruits[1]                           # Remove a specific item
Fruits=("${Fruits[@]}")                   # Copy array
Fruits=("${Fruits[@]}" "${Veggies[@]}")  # Merge with another array
lines=(`cat "datafile"`)                  # Read from a file

Arrays as arguments

function retrieve() {
    local -n arrayRef=$1
    local index=$2
    echo "${arrayRef[$index]}"
}

Fruits=('Grapes' 'Mango' 'Pineapple')
retrieve Fruits 1   # => Mango

Bash Dictionaries

Defining

declare -A animals

animals[cat]="meow"
animals[horse]="neigh"
animals[duck]="quack"
animals[sheep]="baa"

Working with dictionaries

echo ${animals[cat]}  # Cat's sound
echo ${animals[@]}     # All sounds
echo ${!animals[@]}    # All animal names
echo ${#animals[@]}    # Total number of entries
unset animals[cat]     # Remove cat

Iteration

for sound in "${animals[@]}"; do
    echo $sound
done

for animal in "${!animals[@]}"; do
    echo $animal
done

Bash Conditionals

Integer conditions

[[ VALUE -eq VALUE ]]	# Equal
[[ VALUE -ne VALUE ]]	# Not equal
[[ VALUE -lt VALUE ]]	# Less than
[[ VALUE -le VALUE ]]	# Less than or equal
[[ VALUE -gt VALUE ]]	# Greater than
[[ VALUE -ge VALUE ]]	# Greater than or equal
(( VALUE < VALUE ))		# Less than
(( VALUE <= VALUE ))	# Less than or equal
(( VALUE > VALUE ))		# Greater than
(( VALUE >= VALUE ))	# Greater than or equal

String conditions

[[ -z TEXT ]]		# Empty string
[[ -n TEXT ]]		# Not an empty string
[[ TEXT == TEXT ]]	# Equal
[[ TEXT = TEXT ]]	# Equal (same as above)
[[ TEXT < TEXT ]]	# Less than (ASCII comparison)
[[ TEXT > TEXT ]]	# Greater than (ASCII comparison)
[[ TEXT != TEXT ]]	# Not equal
[[ TEXT =~ REGEX ]]	# Regular expression match

Example

# String Check
if [[ -z "$text" ]]; then
    echo "Text is empty"
elif [[ -n "$text" ]]; then
    echo "Text is not empty"
else
    echo "This condition is never met"
fi

# Combinations
if [[ condition1 && condition2 ]]; then
    ...
fi

# Equal
if [[ "$var1" == "$var2" ]]; then
    ...
fi

# Regex Match
if [[ '2. hello' =~ ([a-z]+) ]]; then
    echo ${BASH_REMATCH[1]}
fi

# Comparison
if (( num1 < num2 )); then
    echo "$num1 is less than $num2"
fi

# File Existence
if [[ -e "document.txt" ]]; then
    echo "The file exists"
fi

File conditions

[[ -e FILE ]]     # File exists
[[ -d FILE ]]     # Is a directory
[[ -f FILE ]]     # Is a regular file
[[ -h FILE ]]     # Is a symbolic link
[[ -s FILE ]]     # File size is greater than 0 bytes
[[ -r FILE ]]     # File is readable
[[ -w FILE ]]     # File is writable
[[ -x FILE ]]     # File is executable
[[ f1 -nt f2 ]]   # f1 is newer than f2
[[ f1 -ot f2 ]]   # f2 is older than f1
[[ f1 -ef f2 ]]   # f1 and f2 are the same file

More conditions

[[ -o option ]]    # If the specified option is enabled
[[ ! expression ]] # Not (negation)
[[ condition1 && condition2 ]] # And (both conditions must be true)
[[ condition1 || condition2 ]] # Or (at least one condition must be true)

logical and, or

if [ "$1" = 'yes' -a "$2" -gt 0 ]; then
    echo "Confirmed"
fi

if [ "$1" = 'no' -o "$2" -lt 0 ]; then
    echo "Denied"
fi

Bash Loops

Basic for loop

for file in /var/log/*.log; do
    echo $file
done

C-like for loop

for ((j = 0; j < 50; j++)); do
    echo $j
done

Ranges

for j in {1..5}; do
    echo "Hello $j"
done

# With step size
for j in {10..100..10}; do
    echo "Hello $j"
done

Auto increment

count=1
while [[ $count -le 5 ]]; do
    echo "Count: $count"
    ((count++))
done

Auto decrement

count=3
while [[ $count -gt 0 ]]; do
    echo "Countdown: $count"
    ((count--))
done

Continue

for value in $(seq 1 5); do
    if [[ $value == 4 ]]; then
        continue
    fi
    echo "$value"
done

Break

for value in $(seq 1 5); do
    if [[ $value == 4 ]]; then
        # Exit the loop entirely.
        break
    fi
    # This will print 1, 2, and 3
    echo "$value"
done

Until

number=0
until [ $number -ge 5 ]; do
    echo "$number"
    ((number++))
done

Forever

while true; do
    # Here is some additional code.
done

Forever (shorthand)

while :; do
    # Here is some other code.
done

Reading lines

cat data.txt | while read entry; do
    echo $entry
done

Bash Functions

Defining functions

greet() {
    echo "Hi $1"
}

# Alternate syntax
function greet() {
    echo "Hi $1"
}

greet "Alice"

Returning values

calculate() {
    local output='computed result'
    echo $output
}

result="$(calculate)"

Raising errors

check_status() {
    return 1
}

if check_status; then
    echo "Operation succeeded"
else
    echo "Operation failed"
fi

Bash Options

Options

# Prevents accidental file overwrite
# (echo "hello" > output)
set -o noclobber

# Exits immediately on any command failure
# to prevent cascading issues
set -o errexit   

# Reveals hidden errors in pipelines
set -o pipefail  

# Warns on using unset variables
set -o nounset

Glob options

# Removes non-matching patterns 
# ('*.bar' => '')
shopt -s nullglob

# Triggers an error on non-matching patterns
shopt -s failglob

# Makes pattern matching case-insensitive
shopt -s nocaseglob

# Wildcards will also match hidden files 
# (e.g., "*.txt" => ".hidden.txt")
shopt -s dotglob

# Enables recursive globbing with **
# ('src/**/*.js' => 'src/folder/file.js')
shopt -s globstar

Bash History

Commands

history             # Display command history
sudo !!             # Re-run the last command with elevated permissions
shopt -s histverify # Enable confirmation before executing expanded history commands

Expansions

!$               # Use the final argument from the last command
!*               # Use all arguments from the last command
!-n              # Recall the nth previous command
!n               # Recall the command at position n in history
!<command>       # Run the last instance of the specified command

Operations

!!                # Run the previous command again
!!:s/<FROM>/<TO>/ # Substitute the first <FROM> with <TO> in the last command
!!:gs/<FROM>/<TO>/ # Replace all <FROM> occurrences with <TO> in the last command
!$:t              # Get only the basename of the last argument from the last command
!$:h              # Get only the directory of the last argument from the last command

!!                # Repeats the entire last command
!$                # Expands the last argument of the previous command

Slices

!!:n       # Expands only the nth token from the most recent command (command is 0; first argument is 1)
!^         # Expands the first argument from the most recent command
!$         # Expands the last token from the most recent command
!!:n-m     # Expands a range of tokens from the most recent command (from nth to mth)
!!:n-$     # Expands from the nth token to the last token of the most recent command

!cat       # Repeats the most recent invocation of the `cat` command
!-2        # Expands to the command executed two commands ago
!42        # Expands to the command with history number 42

Miscellaneous

Numeric calculations

$((a + 200))      # Adds 200 to the value of variable 'a'

$(($RANDOM % 200)) # Generates a random number between 0 and 199

Subshells

(cd somedir; echo "I'm now in $PWD") # Changes to 'somedir' temporarily and prints the current directory
pwd # Still shows the original directory

Inspecting commands

command -V cd # Check the type of 'cd' (function, alias, etc.)
#=> "cd is a function/alias/whatever" # Displays the type of the command

Redirection

python hello.py > output.txt      # Redirect standard output to output.txt
python hello.py >> output.txt     # Append standard output to output.txt
python hello.py 2> error.log      # Redirect standard error to error.log
python hello.py 2>&1              # Merge standard error with standard output
python hello.py 2>/dev/null       # Discard standard error output
python hello.py &>/dev/null       # Discard both standard output and standard error

python hello.py < foo.txt         # Provide foo.txt as standard input to python

Source relative

source "${0%/*}/../share/bar.sh"

Directory of script

DIR="${0%/*}"  # Extracts the directory path of the current script

Case/switch

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

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

Trap errors

# Trap errors and display the line number where the error occurred
trap 'echo Error at line $LINENO' ERR

# Function to handle errors with specific details
traperr() {
    echo "ERROR: ${BASH_SOURCE[1]} at line ${BASH_LINENO[0]}"
}

# Enable error tracing
set -o errtrace
trap traperr ERR

printf

printf "Hello %s, I'm %s" Sven Olga
# => "Hello Sven, I'm Olga"

printf "1 + 1 = %d" 2
# => "1 + 1 = 2"

printf "Print a float: %f" 2
# => "Print a float: 2.000000"

Getting options

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

Check for command's result

if ping -c 1 google.com > /dev/null 2>&1; then
    echo "It seems you have a working internet connection."
fi

Grep check

if grep -q 'foo' ~/.bash_history; then
    echo "It seems you've typed 'foo' in the past."
fi

Special variables

$?   # Exit status of the last executed command
$!   # Process ID (PID) of the most recently executed background task
$$   # Process ID (PID) of the current shell
$0   # Name of the shell script being executed

See: Special parameters.

Backslash escapes

\!  # Exclamation mark
\"  # Double quote
\#  # Hash (used for comments)
\&  # Ampersand (used for background processes)
\'  # Single quote
\(  # Opening parenthesis
\)  # Closing parenthesis
\,  # Comma
\;  # Semicolon (used to separate commands)
\<  # Less than (input redirection)
\>  # Greater than (output redirection)
\[  # Opening square bracket (used for test commands)
\|  # Pipe (used to pass output from one command to another)
\\  # Backslash (used for escaping characters)
\]  # Closing square bracket
\^  # Caret (used for bitwise XOR or to denote the start of a line in regex)
\{  # Opening curly brace
\}  # Closing curly brace
\`  # Backtick (used for command substitution)
\$  # Dollar sign (used for variable expansion)
\*  # Asterisk (used as a wildcard)
\?  # Question mark (used as a single-character wildcard)

Heredoc

command << EOF
line 1
line 2
line 3
EOF

Go to previous directory

# Print the current working directory
pwd # Outputs: /home/user/foo

# Change directory to 'bar'
cd bar/

# Print the current working directory again
pwd # Outputs: /home/user/foo/bar

# Change back to the previous directory
cd -

# Print the current working directory to confirm the change
pwd # Outputs: /home/user/foo

Reading input

# Prompt the user for input without a newline
echo -n "Proceed? [y/n]: "

# Read the user's response into the variable 'ans'
read ans

# Print the user's response
echo $ans

# Read a single character from user input and store it in 'ans'
read -n 1 ans  # Just one character

Conditional execution

# Commit changes to the local Git repository and push them to the remote repository
git commit && git push

# If the commit fails, print a message indicating the failure
git commit || echo "Commit failed"

Strict mode

#!/bin/bash

# Enable strict mode
set -o errexit    # Exit immediately if a command exits with a non-zero status
set -o nounset    # Treat unset variables as an error when substituting
set -o pipefail   # Return the exit status of the last command in the pipeline that failed

# Example function
example_function() {
    local name=$1   # Use a local variable to prevent accidental modification of global variables
    echo "Hello, $name!"
}

# Main script
example_function "World"  # Call the function with an argument

# Demonstration of strict mode
# Uncomment the lines below to see strict mode in action

# unset foo          # Uncommenting this will cause an error due to nounset
# echo $foo         # This will not execute if 'foo' is unset

# command_that_fails # Uncommenting this will cause the script to exit immediately due to errexit

See: Unofficial bash strict mode

Optional arguments

#!/bin/bash

# Initialize an array with command-line arguments
args=("$@")  # Store all command-line arguments into the 'args' array

# Append additional elements to the array
args+=(foo)  # Add 'foo' to the array
args+=(bar)  # Add 'bar' to the array

# Print all elements of the array
echo "Array elements: ${args[@]}"  # Output the complete array

Array elements: arg1 arg2 foo bar