Bash Programming Cheat Sheet
Bash (Bourne Again SHell) is a powerful scripting language for Linux automation, system administration, and DevOps workflows.
Script Basics
Shebang and Execution
#!/bin/bash
# This is a comment
echo "Hello, World!"
Make executable and run:
chmod +x script.sh
./script.sh
Strict Mode
#!/bin/bash
set -euo pipefail # Exit on error, undefined vars, pipe failures
IFS=$'\n\t' # Set safe Internal Field Separator
Variables
Declaration and Usage
# Variable assignment (no spaces!)
name="Alice"
age=25
# Using variables
echo "Name: $name"
echo "Age: ${age}"
# Command substitution
today=$(date +%Y-%m-%d)
files=`ls` # Old style (not recommended)
# Readonly variable
readonly PI=3.14159
# Environment variable
export PATH="/usr/local/bin:$PATH"
# Unset variable
unset name
Variable Scopes
# Global variable
global_var="I'm global"
function test() {
local local_var="I'm local"
echo "$global_var" # Works
echo "$local_var" # Works
}
echo "$local_var" # Empty (out of scope)
Special Variables
$0 # Script name
$1-$9 # Arguments 1-9
$# # Number of arguments
$@ # All arguments (as array)
$* # All arguments (as single string)
$? # Exit status of last command
$$ # Current process ID
$! # Last background process ID
$_ # Last argument of previous command
Data Types and Operators
Arithmetic Operations
# Using $(( ))
((a = 5 + 3))
((b = a * 2))
((c++))
echo $((10 / 3)) # 3 (integer division)
# Using let
let "sum = 5 + 3"
let "result = sum * 2"
# Using expr (old method)
result=`expr 5 + 3`
# Operators
+ - * / % # Arithmetic
() ** ++ -- # Grouping, power, increment, decrement
Comparison Operators
# Numeric comparisons
-eq # Equal
-ne # Not equal
-gt # Greater than
-ge # Greater or equal
-lt # Less than
-le # Less or equal
# Example
if [ $age -ge 18 ]; then
echo "Adult"
fi
String Operations
# String comparisons
= or == # Equal
!= # Not equal
< # Less than (alphabetically)
> # Greater than
-z # Empty string
-n # Not empty string
# Examples
if [ "$str1" = "$str2" ]; then
echo "Equal"
fi
if [ -z "$empty" ]; then
echo "String is empty"
fi
# String length
echo ${#string}
# Substring
text="Hello World"
echo ${text:0:5} # "Hello"
echo ${text:6} # "World"
# Replace
echo ${text/World/Bash} # Replace first
echo ${text//o/O} # Replace all
# Remove prefix/suffix
file="document.txt"
echo ${file%.txt} # "document"
echo ${file#doc} # "ument.txt"
echo ${file%.*} # "document"
echo ${file##*.} # "txt"
# Case conversion
echo ${text^^} # HELLO WORLD
echo ${text,,} # hello world
echo ${text^} # Hello world (first char)
Logical Operators
&& # AND
|| # OR
! # NOT
# Examples
if [ $a -gt 5 ] && [ $b -lt 10 ]; then
echo "Both conditions true"
fi
if [[ $a > 5 && $b < 10 ]]; then
echo "Using [[ ]] (preferred)"
fi
Conditionals
If Statement
# Basic if
if [ condition ]; then
commands
fi
# If-else
if [ condition ]; then
commands
else
commands
fi
# If-elif-else
if [ "$age" -lt 13 ]; then
echo "Child"
elif [ "$age" -lt 18 ]; then
echo "Teen"
else
echo "Adult"
fi
# One-liner
[ $a -eq 1 ] && echo "One" || echo "Not one"
Test Commands
# [ ] vs [[ ]]
[ $a = $b ] # POSIX, requires quotes
[[ $a = $b ]] # Bash, no quote needed, supports regex
# File tests
-e file # Exists
-f file # Regular file
-d file # Directory
-s file # Not empty
-r file # Readable
-w file # Writable
-x file # Executable
-L file # Symbolic link
-S file # Socket
-p file # Named pipe
# Examples
if [ -f "file.txt" ]; then
echo "File exists"
fi
if [[ -r "config.cfg" && -w "config.cfg" ]]; then
echo "Can read and write"
fi
Case Statement
case $variable in
pattern1)
commands
;;
pattern2|pattern3)
commands
;;
*)
default commands
;;
esac
# Example
read -p "Enter fruit: " fruit
case $fruit in
apple)
echo "Red or green"
;;
banana)
echo "Yellow"
;;
orange|lemon|lime)
echo "Citrus"
;;
*)
echo "Unknown fruit"
;;
esac
Loops
For Loop
# List iteration
for item in apple banana cherry; do
echo "$item"
done
# Range
for i in {1..10}; do
echo "$i"
done
# Step range
for i in {0..20..2}; do
echo "$i" # 0, 2, 4, ..., 20
done
# C-style
for ((i=0; i<10; i++)); do
echo "$i"
done
# File iteration
for file in *.txt; do
echo "Processing: $file"
done
# Array iteration
fruits=(apple banana orange)
for fruit in "${fruits[@]}"; do
echo "$fruit"
done
# With index
for i in "${!fruits[@]}"; do
echo "$i: ${fruits[$i]}"
done
While Loop
# Basic while
count=1
while [ $count -le 5 ]; do
echo "Count: $count"
((count++))
done
# Read file line by line
while IFS= read -r line; do
echo "Line: $line"
done < file.txt
# Infinite loop
while true; do
echo "Press Ctrl+C to stop"
sleep 1
done
Until Loop
count=0
until [ $count -ge 5 ]; do
echo "$count"
((count++))
done
Loop Control
# Break - exit loop
for i in {1..10}; do
[ $i -eq 5 ] && break
echo "$i"
done
# Continue - skip iteration
for i in {1..10}; do
[ $((i % 2)) -eq 0 ] && continue
echo "$i" # Only odd numbers
done
Functions
Function Definition
# Method 1
function greet() {
echo "Hello, $1!"
}
# Method 2 (preferred)
greet() {
echo "Hello, $1!"
}
# Call function
greet "Alice"
Function Arguments
process_file() {
local filename=$1
local option=${2:-default} # Default value
echo "Processing: $filename"
echo "Option: $option"
}
process_file "data.txt" "verbose"
Return Values
# Return exit code (0-255)
is_valid() {
[ -f "$1" ] && return 0 || return 1
}
if is_valid "file.txt"; then
echo "Valid"
fi
# Return string via echo
get_date() {
echo $(date +%Y-%m-%d)
}
today=$(get_date)
echo "Today: $today"
# Multiple return values
get_user_info() {
echo "John Doe 30"
}
read name surname age < <(get_user_info)
Recursive Functions
factorial() {
local n=$1
if [ $n -le 1 ]; then
echo 1
else
local prev=$(factorial $((n-1)))
echo $((n * prev))
fi
}
result=$(factorial 5) # 120
Arrays
Indexed Arrays
# Declaration
arr=(apple banana cherry)
arr[3]="date"
# Access elements
echo ${arr[0]} # First element
echo ${arr[-1]} # Last element
echo ${arr[@]} # All elements
echo ${arr[*]} # All elements (different quoting)
# Array length
echo ${#arr[@]} # Number of elements
echo ${#arr[0]} # Length of first element
# Add elements
arr+=(elderberry)
arr[10]="fig" # Sparse array
# Remove element
unset arr[1]
# Slicing
echo ${arr[@]:1:2} # Elements from index 1, count 2
# Iteration
for item in "${arr[@]}"; do
echo "$item"
done
# With indices
for i in "${!arr[@]}"; do
echo "$i: ${arr[$i]}"
done
Associative Arrays (Dictionaries)
# Declare
declare -A colors
# Assign
colors[apple]="red"
colors[banana]="yellow"
colors[grape]="purple"
# Access
echo ${colors[apple]}
# All keys
echo ${!colors[@]}
# All values
echo ${colors[@]}
# Iterate
for fruit in "${!colors[@]}"; do
echo "$fruit is ${colors[$fruit]}"
done
# Check if key exists
if [[ -v colors[apple] ]]; then
echo "Apple exists"
fi
Input/Output
Reading Input
# Basic read
read name
echo "Hello, $name"
# With prompt
read -p "Enter age: " age
# Silent input (passwords)
read -sp "Password: " password
echo
# Multiple variables
read -p "Enter first and last name: " first last
# Read array
read -a words
echo "First word: ${words[0]}"
# Read with timeout
read -t 5 -p "Quick (5s): " answer
# Read single character
read -n 1 -p "Press any key"
Output
# Echo
echo "Simple output"
echo -n "No newline"
echo -e "Tab:\tNewline:\n"
# Printf (formatted)
printf "Name: %s, Age: %d\n" "Alice" 25
printf "%.2f\n" 3.14159
# Redirect output
echo "text" > file.txt # Overwrite
echo "text" >> file.txt # Append
echo "error" >&2 # To stderr
# Here document
cat << EOF
Multiple
Lines
EOF
# Here string
wc -l <<< "Count this line"
File Operations
Reading Files
# Read entire file
content=$(cat file.txt)
# Read line by line
while IFS= read -r line; do
echo "$line"
done < file.txt
# Read into array
mapfile -t lines < file.txt
readarray -t lines < file.txt # Alternative
Writing Files
# Overwrite
echo "New content" > file.txt
# Append
echo "More content" >> file.txt
# Multi-line write
cat > config.txt << EOF
setting1=value1
setting2=value2
EOF
File Testing
if [ -e file.txt ]; then
echo "Exists"
fi
if [ ! -f file.txt ]; then
echo "Not a regular file"
exit 1
fi
# Combine checks
if [[ -f file.txt && -r file.txt ]]; then
cat file.txt
fi
Process Management
Running Commands
# Background process
long_command &
pid=$!
echo "Running in background: $pid"
# Wait for background process
wait $pid
# Command grouping
(cd /tmp && ls) # Subshell
{ cd /tmp; ls; } # Current shell
# Conditional execution
mkdir mydir && cd mydir
rm file.txt || echo "Delete failed"
Pipelines
# Pipe output
ls -l | grep ".txt"
# Pipe to multiple commands
cat file.txt | grep "error" | sort | uniq
# Tee (output to file and stdout)
ls -l | tee output.txt
# Process substitution
diff <(ls dir1) <(ls dir2)
Error Handling
Exit Codes
# Check last command
if [ $? -eq 0 ]; then
echo "Success"
else
echo "Failed"
fi
# Or directly
if command; then
echo "Success"
fi
# Set exit code
exit 0 # Success
exit 1 # Error
Error Handling Patterns
# Exit on error
set -e
# Exit on undefined variable
set -u
# Exit on pipe failure
set -o pipefail
# Disable temporarily
set +e
risky_command
set -e
# Trap errors
trap 'echo "Error on line $LINENO"' ERR
# Trap exit
trap 'cleanup_function' EXIT
# Trap signals
trap 'echo "Interrupted"; exit' INT TERM
Debugging
Debug Techniques
# Print each command
set -x
commands here
set +x
# Verbose mode
set -v
# Run script in debug mode
bash -x script.sh
# Check syntax only
bash -n script.sh
# Custom debug function
DEBUG=true
debug() {
[[ "$DEBUG" == "true" ]] && echo "[DEBUG] $*" >&2
}
debug "This is a debug message"
Practical Patterns
Argument Parsing
#!/bin/bash
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
echo "Usage: $0 [options]"
exit 0
;;
-v|--verbose)
VERBOSE=true
shift
;;
-f|--file)
FILE="$2"
shift 2
;;
*)
echo "Unknown option: $1"
exit 1
;;
esac
done
Configuration Files
# Load config
if [ -f config.sh ]; then
source config.sh
# or
. config.sh
fi
# Parse INI-style config
while IFS='=' read -r key value; do
[[ $key =~ ^[[:space:]]*# ]] && continue
[[ -z $key ]] && continue
declare "$key=$value"
done < config.ini
Logging
#!/bin/bash
LOG_FILE="/var/log/script.log"
log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}
log "Script started"
log "INFO" "Processing files..."
log "ERROR" "Failed to connect"
Best Practices
Script Template
#!/bin/bash
#
# Script: script_name.sh
# Description: What this script does
# Author: Your Name
# Date: 2025-02-08
#
set -euo pipefail
IFS=$'\n\t'
# Constants
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly SCRIPT_NAME="$(basename "$0")"
# Functions
usage() {
cat << EOF
Usage: $SCRIPT_NAME [OPTIONS]
OPTIONS:
-h, --help Show help
-v, --verbose Verbose output
EOF
}
main() {
# Main logic
echo "Running..."
}
# Run
main "$@"
Coding Standards
# Use meaningful names
user_count=10 # Good
x=10 # Bad
# Quote variables
rm "$file" # Good
rm $file # Bad (breaks with spaces)
# Use [[ ]] over [ ]
if [[ $var == "value" ]]; then
echo "Match"
fi
# UPPERCASE for constants
readonly MAX_RETRIES=3
# lowercase for local variables
local temp_file="/tmp/data"
# Check if variable is set
if [[ -n "${VAR:-}" ]]; then
echo "VAR is set"
fi
# Use functions for reusable code
# Use comments for complex logic
# Keep lines under 80 characters
Quick Reference
# Variables
var="value"
echo "$var"
readonly CONST="constant"
# Arithmetic
((result = 5 + 3))
echo $((10 / 3))
# Conditionals
if [[ condition ]]; then
commands
elif [[ condition ]]; then
commands
else
commands
fi
# Loops
for i in {1..10}; do
echo "$i"
done
while [[ condition ]]; do
commands
done
# Functions
function_name() {
local var=$1
echo "$var"
}
# Arrays
arr=(item1 item2 item3)
echo "${arr[0]}"
echo "${arr[@]}"
# Strings
${#string} # Length
${string:pos:len} # Substring
${string//find/replace} # Replace all
# File tests
-e file # Exists
-f file # Is file
-d file # Is directory
-r file # Readable
# Exit codes
$? # Last exit code
exit 0 # Exit success
exit 1 # Exit failure