Bash Scripting Cheatsheet

A comprehensive Bash scripting reference guide covering variables, loops, functions, conditionals, arrays, and more. Quick reference for all your shell scripting needs.
Bash Scripting Cheatsheet
Getting Started
Introduction
This is a comprehensive quick reference to Bash scripting. Bash is a Unix shell and command language that provides a powerful interface for interacting with your operating system.
Useful Resources:
Hello World Example
#!/usr/bin/env bash
name="John"
echo "Hello $name!"Variables
name="John"
echo $name # John
echo "$name" # John (recommended)
echo "${name}!" # John!Best Practice: Generally quote your variables unless they contain wildcards to expand or command fragments.
wildcard="*.txt"
options="iv"
cp -$options $wildcard /tmpString Quotes
name="John"
echo "Hi $name" # Hi John (variables expand)
echo 'Hi $name' # Hi $name (no expansion)Shell Execution
echo "I'm in $(pwd)"
echo "I'm in `pwd`" # obsolescent syntax
# Both produce the same outputConditional Execution
git commit && git push # Run push only if commit succeeds
git commit || echo "Commit failed" # Run echo only if commit failsStrict Mode
Enable strict mode for safer scripts:
set -euo pipefail
IFS=$'\n\t'This enables:
errexit: Exit on errornounset: Error on unset variablespipefail: Catch errors in pipes
Brace Expansion
echo {A,B}.js # A.js B.js
echo {1..5} # 1 2 3 4 5
echo {{1..3},{7..9}} # 1 2 3 7 8 9| Expression | Description | Output |
|---|---|---|
{A,B} | Expand alternatives | A B |
{A,B}.js | With suffix | A.js B.js |
{1..5} | Numeric range | 1 2 3 4 5 |
{1..10..2} | Range with step | 1 3 5 7 9 |
Parameter Expansions
Basic Expansions
name="John"
echo "${name}" # John
echo "${name/J/j}" # john (substitution)
echo "${name:0:2}" # Jo (slicing)
echo "${name::2}" # Jo (slicing)
echo "${name::-1}" # Joh (remove last char)
echo "${name:(-1)}" # n (last char)
echo "${name:(-2):1}" # h (second to last char)
echo "${food:-Cake}" # $food or "Cake" if unsetString Manipulation
str="/path/to/foo.cpp"
# Remove suffix
echo "${str%.cpp}" # /path/to/foo
echo "${str%.cpp}.o" # /path/to/foo.o
echo "${str%/*}" # /path/to
# Extract extension and basename
echo "${str##*.}" # cpp
echo "${str##*/}" # foo.cpp
# Remove prefix
echo "${str#*/}" # path/to/foo.cpp
echo "${str##*/}" # foo.cpp
# Replace
echo "${str/foo/bar}" # /path/to/bar.cppSlicing
str="Hello world"
echo "${str:6:5}" # world
echo "${str: -5:5}" # world (from end)Path Manipulation
src="/path/to/foo.cpp"
base=${src##*/} # foo.cpp (basename)
dir=${src%$base} # /path/to/ (directory path)
dir=${src%/*} # /path/to (directory)Substitution Reference
| Code | Description |
|---|---|
${foo%suffix} | Remove shortest suffix |
${foo%%suffix} | Remove longest suffix |
${foo#prefix} | Remove shortest prefix |
${foo##prefix} | Remove longest prefix |
${foo/from/to} | Replace first match |
${foo//from/to} | Replace all matches |
${foo/%from/to} | Replace suffix |
${foo/#from/to} | Replace prefix |
Length
str="Hello"
echo "${#str}" # 5 (string length)Case Modification
str="HELLO WORLD!"
echo "${str,}" # hELLO WORLD! (lowercase first)
echo "${str,,}" # hello world! (all lowercase)
str="hello world!"
echo "${str^}" # Hello world! (uppercase first)
echo "${str^^}" # HELLO WORLD! (all uppercase)Default Values
| Expression | Description |
|---|---|
${foo:-val} | $foo, or val if unset/null |
${foo:=val} | Set $foo to val if unset/null |
${foo:+val} | val if $foo is set |
${foo:?message} | Show error and exit if unset/null |
Comments
# Single line comment
: '
This is a
multi-line
comment
'Loops
Basic For Loop
for i in /etc/rc.*; do
echo "$i"
doneC-style For Loop
for ((i = 0; i < 100; i++)); do
echo "$i"
doneRanges
for i in {1..5}; do
echo "Welcome $i"
doneWith Step Size
for i in {5..50..5}; do
echo "Welcome $i"
doneReading Lines
while read -r line; do
echo "$line"
done < file.txtInfinite Loop
while true; do
# Your code here
sleep 1
doneUntil Loop
count=0
until [ $count -gt 5 ]; do
echo "Count: $count"
((count++))
doneFunctions
Defining Functions
# Method 1
myfunc() {
echo "hello $1"
}
# Method 2
function myfunc {
echo "hello $1"
}
# Call the function
myfunc "John"Returning Values
myfunc() {
local myresult='some value'
echo "$myresult"
}
result=$(myfunc)
echo "$result"Return Status
myfunc() {
return 1 # Return exit code
}
if myfunc; then
echo "success"
else
echo "failure"
fiFunction Arguments
| Expression | Description |
|---|---|
$# | Number of arguments |
$* | All positional arguments (as single word) |
$@ | All positional arguments (as separate strings) |
$1 | First argument |
$_ | Last argument of previous command |
Note: $@ and $* must be quoted to preserve behavior.
Conditionals
String Conditions
| Condition | Description |
|---|---|
[[ -z STRING ]] | Empty string |
[[ -n STRING ]] | Not empty string |
[[ STRING == STRING ]] | Equal |
[[ STRING != STRING ]] | Not equal |
[[ STRING =~ REGEX ]] | Regex match |
Numeric Conditions
| Condition | Description |
|---|---|
[[ NUM -eq NUM ]] | Equal |
[[ NUM -ne NUM ]] | Not equal |
[[ NUM -lt NUM ]] | Less than |
[[ NUM -le NUM ]] | Less than or equal |
[[ NUM -gt NUM ]] | Greater than |
[[ NUM -ge NUM ]] | Greater than or equal |
(( NUM < NUM )) | Numeric comparison |
File Conditions
| Condition | Description |
|---|---|
[[ -e FILE ]] | Exists |
[[ -f FILE ]] | Regular file |
[[ -d FILE ]] | Directory |
[[ -h FILE ]] | Symlink |
[[ -r FILE ]] | Readable |
[[ -w FILE ]] | Writable |
[[ -x FILE ]] | Executable |
[[ -s FILE ]] | Size is > 0 bytes |
[[ FILE1 -nt FILE2 ]] | FILE1 is newer than FILE2 |
[[ FILE1 -ot FILE2 ]] | FILE1 is older than FILE2 |
[[ FILE1 -ef FILE2 ]] | Same files |
Logical Operators
| Condition | Description |
|---|---|
[[ ! EXPR ]] | Not |
[[ X && Y ]] | And |
| `[[ X |
If Statement Examples
# String check
if [[ -z "$string" ]]; then
echo "String is empty"
elif [[ -n "$string" ]]; then
echo "String is not empty"
fi
# Combinations
if [[ X && Y ]]; then
echo "Both true"
fi
# Numeric comparison
if (( $a < $b )); then
echo "$a is smaller than $b"
fi
# File exists
if [[ -e "file.txt" ]]; then
echo "file exists"
fi
# Regex
if [[ "$email" =~ ^[a-zA-Z0-9]+@[a-zA-Z0-9]+\.[a-zA-Z]{2,}$ ]]; then
echo "Valid email"
fiArrays
Defining Arrays
# Method 1
Fruits=('Apple' 'Banana' 'Orange')
# Method 2
Fruits[0]="Apple"
Fruits[1]="Banana"
Fruits[2]="Orange"Accessing Arrays
echo "${Fruits[0]}" # Element #0
echo "${Fruits[-1]}" # Last element
echo "${Fruits[@]}" # All elements
echo "${#Fruits[@]}" # Number of elements
echo "${#Fruits}" # String length of 1st element
echo "${#Fruits[3]}" # String length of Nth element
echo "${Fruits[@]:3:2}" # Range (from position 3, length 2)
echo "${!Fruits[@]}" # All indicesArray Operations
# Append
Fruits=("${Fruits[@]}" "Watermelon")
Fruits+=('Watermelon')
# Remove by regex
Fruits=( "${Fruits[@]/Ap*/}" )
# Remove one item
unset Fruits[2]
# Duplicate
Fruits=("${Fruits[@]}")
# Concatenate
Fruits=("${Fruits[@]}" "${Veggies[@]}")
# Read from file
lines=($(< file.txt))Iteration
for i in "${arrayName[@]}"; do
echo "$i"
doneDictionaries (Associative Arrays)
Defining Dictionaries
declare -A sounds
sounds[dog]="bark"
sounds[cow]="moo"
sounds[bird]="tweet"
sounds[wolf]="howl"Working with Dictionaries
echo "${sounds[dog]}" # Dog's sound
echo "${sounds[@]}" # All values
echo "${!sounds[@]}" # All keys
echo "${#sounds[@]}" # Number of elements
unset sounds[dog] # Delete dogIteration
# Iterate over values
for val in "${sounds[@]}"; do
echo "$val"
done
# Iterate over keys
for key in "${!sounds[@]}"; do
echo "$key - ${sounds[$key]}"
doneOptions and Settings
Set Options
set -o noclobber # Prevent file overwrite with >
set -o errexit # Exit on error
set -o pipefail # Catch errors in pipes
set -o nounset # Error on unset variablesShopt Options
shopt -s nullglob # Non-matching globs expand to nothing
shopt -s failglob # Non-matching globs throw errors
shopt -s nocaseglob # Case insensitive globs
shopt -s dotglob # Match hidden files with *
shopt -s globstar # Enable ** for recursive matchingHistory
History Commands
| Command | Description |
|---|---|
history | Show command history |
shopt -s histverify | Don't execute expanded result immediately |
History Expansions
| Expression | Description |
|---|---|
!! | Last command |
!$ | Last argument of last command |
!* | All arguments of last command |
!-n | nth most recent command |
!n | nth command in history |
!string | Most recent command starting with string |
History Operations
| Code | Description |
|---|---|
!!:s/FROM/TO/ | Replace first occurrence |
!!:gs/FROM/TO/ | Replace all occurrences |
!$:t | Basename of last argument |
!$:h | Directory of last argument |
Input/Output Redirection
Basic Redirection
# Redirect stdout to file
command > output.txt
# Append stdout to file
command >> output.txt
# Redirect stderr to file
command 2> error.log
# Redirect stderr to stdout
command 2>&1
# Redirect both stdout and stderr
command &> output.txt
command > output.txt 2>&1
# Redirect to null (discard)
command > /dev/null
command 2> /dev/null
command &> /dev/null
# Read from file
command < input.txtAdvanced Redirection
# Compare two outputs
diff <(ls dir1) <(ls dir2)
# Process substitution
while read -r line; do
echo "$line"
done < <(command)Heredoc
cat <<EOF
This is a
multi-line
text block
EOFHerestring
tr '[:lower:]' '[:upper:]' <<< "hello world"
# Output: HELLO WORLDMiscellaneous
Arithmetic
# Addition
echo $((a + 200))
# Random number
echo $((RANDOM % 200)) # 0-199
# Increment
count=$((count + 1))
((count++))
# Declare integer
declare -i count
count+=1Case Statement
case "$1" in
start | up)
echo "Starting..."
;;
stop | down)
echo "Stopping..."
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
;;
esacPrintf
printf "Hello %s, I'm %s\n" Sven Olga
# Output: Hello Sven, I'm Olga
printf "Number: %d\n" 42
# Output: Number: 42
printf "Float: %.2f\n" 3.14159
# Output: Float: 3.14Reading Input
# Read with prompt
echo -n "Proceed? [y/n]: "
read -r ans
echo "$ans"
# Read single character
read -n 1 ans
# Read with timeout
read -t 5 ans # 5 second timeout
# Read password (hidden)
read -s passwordSpecial Variables
| Variable | Description |
|---|---|
$? | Exit status of last command |
$! | PID of last background task |
$$ | PID of current shell |
$0 | Name of the script |
$1 to $9 | Script arguments |
$# | Number of arguments |
$@ | All arguments (as separate words) |
$* | All arguments (as single word) |
$_ | Last argument of previous command |
${PIPESTATUS[@]} | Exit status of each piped command |
Subshells
# Execute in subshell (doesn't affect current directory)
(cd somedir; echo "I'm now in $PWD")
pwd # Still in original directoryError Trapping
# Simple error trap
trap 'echo "Error at line $LINENO"' ERR
# Advanced error trap
traperr() {
echo "ERROR: ${BASH_SOURCE[1]} at line ${BASH_LINENO[0]}"
}
set -o errtrace
trap traperr ERRCommand Substitution
# Modern syntax (recommended)
result=$(command)
# Old syntax (avoid)
result=`command`Process Substitution
# Compare outputs
diff <(ls dir1) <(ls dir2)
# Loop through command output
while read -r line; do
echo "$line"
done < <(find . -name "*.txt")String Transformations with tr
# Convert to uppercase
echo "hello" | tr '[:lower:]' '[:upper:]'
# Delete characters
echo "hello123world" | tr -d '[:digit:]'
# Squeeze repeated characters
echo "hellooo world" | tr -s 'o 'Checking Commands
# Check if command succeeded
if ping -c 1 google.com &> /dev/null; then
echo "Internet connection available"
fi
# Check if string in file
if grep -q 'pattern' file.txt; then
echo "Pattern found"
fi
# Check if command exists
if command -v git &> /dev/null; then
echo "Git is installed"
fiDirectory Navigation
# Go to previous directory
cd -
# Go to home directory
cd ~
cd
# Go to directory of script
cd "$(dirname "$0")"Best Practices
Script Template
#!/usr/bin/env bash
# Exit on error, undefined variables, and pipe failures
set -euo pipefail
# Set IFS for safer word splitting
IFS=$'\n\t'
# Script description
# Usage: ./script.sh [options]
# Functions
main() {
# Your main logic here
echo "Hello, World!"
}
# Run main function
main "$@"Debugging
# Enable debug mode
set -x
# Debug a specific section
set -x
# code to debug
set +x
# Run with debug
bash -x script.shError Handling
# Check if file exists before reading
if [[ ! -f "$filename" ]]; then
echo "Error: File not found: $filename" >&2
exit 1
fi
# Validate number of arguments
if [[ $# -ne 2 ]]; then
echo "Usage: $0 <arg1> <arg2>" >&2
exit 1
fiUseful One-Liners
# Find and delete files
find . -name "*.tmp" -delete
# Archive and compress
tar -czf archive.tar.gz directory/
# Extract archive
tar -xzf archive.tar.gz
# Find large files
find . -type f -size +100M
# Monitor file changes
watch -n 1 'ls -lh file.txt'
# Quick HTTP server
python3 -m http.server 8000
# Count lines of code
find . -name '*.sh' | xargs wc -l
# Replace in multiple files
find . -name "*.txt" -exec sed -i 's/old/new/g' {} +Additional Resources
- Bash Hackers Wiki
- Shell Vars Reference
- Learn Bash in Y Minutes
- Bash Guide
- ShellCheck - Online shell script analyzer
- Explain Shell - Write down a command to see help text
Happy Scripting! 🚀

Written by Robin te Hofstee
Technology enthusiast with a deep love for programming. Sharing practical guides on React, Next.js, Linux and self-hosting.
Related Posts
Securing a Next.js Application in Production
Securing a Next.js app in production means protecting the client bundle, the server, and the data flow. This post shows how to lock down Content‑Secur
How Git Works
Git is a distributed version control system that allows multiple people to collaborate on a project while maintaining a complete history of all change
How to Check Disk Usage and Find Large Files on Linux
After setting up a new Linux server, you’ll want to know how to check what’s taking up space. Disk space is often the weakest link in system stability


