Quick reference guide for Bash. Essential syntax, control flow, variables, arrays, and common patterns for shell scripting.
Hello World
#!/bin/bash
echo "Hello, World!" # The shebang tells the system which interpreter to use
Variables
name="Alice" # No spaces around =; quotes protect spaces
x=42 # Numbers are strings until you do math
readonly PI=3.14 # Make variable read-only
unset name # Delete variable
echo $name # Access with $ prefix
echo ${name} # Braces are safer for variable expansion
echo "${name}_suffix" # Use braces when concatenating
Control Flow
If/Else
if [ $x -gt 10 ]; then # [ ] requires spaces; -gt means "greater than"
echo "x is big"
else
echo "x is small"
fi
# Comparison operators: -eq, -ne, -lt, -le, -gt, -ge
# String comparison: =, !=, -z (empty), -n (non-empty)
if [ -f "file.txt" ]; then # -f checks if file exists
echo "File exists"
elif [ -d "dir" ]; then # -d checks if directory exists
echo "Directory exists"
fi
For Loops
for i in 1 2 3 4 5; do
echo $i
done
for i in {1..5}; do # Brace expansion: 1 to 5
echo $i
done
for file in *.txt; do # Iterate over files
echo "Processing $file"
done
for ((i=0; i<5; i++)); do # C-style for loop
echo $i
done
While Loops
while [ $x -gt 0 ]; do
echo $x
x=$((x - 1)) # Arithmetic expansion
done
# Read file line by line
while IFS= read -r line; do
echo "$line"
done < file.txt
Case Statement
case $choice in
"1")
echo "Option 1"
;;
"2"|"two")
echo "Option 2"
;;
*)
echo "Default case"
;;
esac
Functions
function greet() {
local name=$1 # $1 is first argument; local limits scope
echo "Hello, $name!"
}
greet "Alice" # Call function
# Alternative syntax
add() {
local a=$1
local b=$2
return $((a + b)) # Return exit code (0-255)
}
add 2 3
result=$? # $? is exit code of last command
Arrays
fruits=("apple" "banana" "cherry") # Declare array
fruits[3]="date" # Add element
echo ${fruits[0]} # Access element (apple)
echo ${fruits[@]} # All elements
echo ${#fruits[@]} # Array length
for fruit in "${fruits[@]}"; do # Iterate over array
echo $fruit
done
Strings
text="Hello, World!"
echo ${#text} # String length
echo ${text:0:5} # Substring: start at 0, length 5 ("Hello")
echo ${text/World/Bash} # Replace first match ("Hello, Bash!")
echo ${text//l/L} # Replace all matches
echo ${text#Hello, } # Remove shortest prefix match
echo ${text##Hello, } # Remove longest prefix match
echo ${text% World!} # Remove shortest suffix match
echo ${text%% World!} # Remove longest suffix match
Input / Output
read name # Read user input
echo "Enter your name: "
read -p "Name: " name # Prompt and read in one line
read -s password # Silent input (for passwords)
read -a array # Read into array
echo "Hello" # Print to stdout
echo -n "No newline" # -n suppresses newline
printf "Value: %d\n" 42 # Formatted output
Command Substitution
date=$(date) # Capture command output (modern syntax)
date=`date` # Backticks (older syntax, less preferred)
files=$(ls *.txt) # List of files
count=$(wc -l < file.txt) # Line count
# Process substitution
diff <(sort file1.txt) <(sort file2.txt)
File Operations
# File tests
[ -f "file.txt" ] # File exists and is regular file
[ -d "dir" ] # Directory exists
[ -r "file.txt" ] # File is readable
[ -w "file.txt" ] # File is writable
[ -x "file.txt" ] # File is executable
[ -s "file.txt" ] # File exists and is not empty
# Reading files
while IFS= read -r line; do
echo "$line"
done < file.txt
# Writing files
echo "content" > file.txt # Overwrite
echo "content" >> file.txt # Append
Arithmetic
x=10
y=5
# Arithmetic expansion
result=$((x + y)) # Addition
result=$((x - y)) # Subtraction
result=$((x * y)) # Multiplication
result=$((x / y)) # Division
result=$((x % y)) # Modulo
result=$((x ** 2)) # Exponentiation
# Increment/decrement
((x++)) # Post-increment
((++x)) # Pre-increment
((x--)) # Decrement
Error Handling
set -e # Exit on error
set -u # Exit on undefined variable
set -o pipefail # Exit on pipe failure
# Trap errors
trap 'echo "Error occurred"' ERR
# Check command success
if command; then
echo "Success"
else
echo "Failed"
fi
# Check exit code
command
if [ $? -eq 0 ]; then
echo "Success"
fi
Tips
- Always quote variables to prevent word splitting:
"$var"not$var.
- Use
[[ ]]instead of[ ]for more features and fewer surprises (Bash 4+).
- Use
localin functions to avoid polluting global scope.
- Test file existence with
-f(file) or-d(directory) before operations.
- Use
$(( ))for arithmetic; it's safer thanexpr.
- Prefer
$(command)over backticks for command substitution.
- Use
read -rto prevent backslash interpretation when reading lines.
- Set
IFS=when reading to preserve leading/trailing whitespace.
- Use
set -eat the start of scripts to exit on errors (unless you handle them explicitly).
- Always start scripts with
#!/bin/bash(or appropriate shebang) for portability.
- Use
${var:-default}to provide default values:name=${1:-"World"}.
- Check if a command exists before using it:
if command -v git &> /dev/null; then git --version fi