Summary
Shell scripting is the cornerstone of Linux/Unix system administration, enabling automation of repetitive tasks and complex workflows. This cheatsheet covers essential Bash scripting concepts, patterns, and best practices critical for system administration interviews. Master variables, control structures, text processing, error handling, and production-ready scripting techniques that demonstrate your ability to automate infrastructure management and solve real-world operational challenges.
Shell Basics
Shebang
#!/bin/bash # Bash shell
#!/bin/sh # POSIX shell
#!/usr/bin/env bash # Portable shebang
Script Execution
chmod +x script.sh # Make executable
./script.sh # Run script
bash script.sh # Run with bash
source script.sh # Run in current shell
. script.sh # Same as source
Comments
# Single line comment
: '
Multi-line
comment
'
Variables
Variable Assignment
NAME="John" # No spaces around =
readonly CONST=100 # Constant variable
local var="local" # Local to function
export PATH=$PATH:/new/path # Export to environment
Variable Types
# String
NAME="Alice"
# Integer
declare -i NUM=42
# Array
declare -a ARR=(1 2 3)
# Associative array (bash 4+)
declare -A DICT=([key1]=val1 [key2]=val2)
Variable Expansion
${VAR} # Value of VAR
${VAR:-default} # Use default if VAR unset/null
${VAR:=default} # Set VAR to default if unset/null
${VAR:?error_msg} # Exit with error if VAR unset/null
${VAR:+alt_value} # Use alt_value if VAR is set
${#VAR} # Length of VAR
${VAR:offset:length} # Substring
${VAR#pattern} # Remove shortest match from start
${VAR##pattern} # Remove longest match from start
${VAR%pattern} # Remove shortest match from end
${VAR%%pattern} # Remove longest match from end
${VAR/old/new} # Replace first occurrence
${VAR//old/new} # Replace all occurrences
Special Variables
$0 # Script name
$1-$9 # Positional parameters
${10} # 10th parameter (need braces)
$# # Number of arguments
$@ # All arguments (array-like)
$* # All arguments (single string)
$$ # Current process ID
$! # Last background process ID
$? # Exit status of last command
$- # Current shell options
Operators
Arithmetic Operators
# Using $(( ))
result=$((5 + 3))
result=$((10 - 2))
result=$((4 * 3))
result=$((10 / 2))
result=$((10 % 3))
result=$((2 ** 3)) # Power
# Using let
let "result = 5 + 3"
# Using expr (older)
result=$(expr 5 + 3)
Comparison Operators
Numeric Comparison
-eq # Equal
-ne # Not equal
-gt # Greater than
-ge # Greater than or equal
-lt # Less than
-le # Less than or equal
# Example
if [ $a -gt $b ]; then
echo "$a is greater"
fi
String Comparison
= # Equal (POSIX)
== # Equal (bash)
!= # Not equal
< # Less than (lexicographic)
> # Greater than (lexicographic)
-z # String is empty
-n # String is not empty
# Example
if [ "$str1" = "$str2" ]; then
echo "Strings are equal"
fi
File Test Operators
-e FILE # Exists
-f FILE # Regular file
-d FILE # Directory
-s FILE # File size > 0
-r FILE # Readable
-w FILE # Writable
-x FILE # Executable
-L FILE # Symbolic link
-p FILE # Named pipe
-S FILE # Socket
FILE1 -nt FILE2 # FILE1 newer than FILE2
FILE1 -ot FILE2 # FILE1 older than FILE2
Logical Operators
&& # AND
|| # OR
! # NOT
# In test expressions
[ $a -gt 0 ] && [ $b -gt 0 ] # Both conditions
[ $a -gt 0 ] || [ $b -gt 0 ] # Either condition
[ ! -f file.txt ] # NOT condition
Control Structures
If-Else
if [ condition ]; then
commands
elif [ condition ]; then
commands
else
commands
fi
# One-liner
[ -f file.txt ] && echo "File exists" || echo "File not found"
Case Statement
case $VAR in
pattern1)
commands ;;
pattern2|pattern3)
commands ;;
*)
default_commands ;;
esac
For Loop
# C-style
for ((i=0; i<10; i++)); do
echo $i
done
# For-in loop
for item in list1 list2 list3; do
echo $item
done
# Array iteration
for element in "${array[@]}"; do
echo $element
done
# File iteration
for file in *.txt; do
echo "Processing $file"
done
While Loop
while [ condition ]; do
commands
done
# Read file line by line
while IFS= read -r line; do
echo "$line"
done < file.txt
Until Loop
until [ condition ]; do
commands
done
Select Menu
select option in "Option1" "Option2" "Quit"; do
case $option in
"Option1") echo "You chose 1" ;;
"Option2") echo "You chose 2" ;;
"Quit") break ;;
esac
done
Functions
Function Definition
# Method 1
function_name() {
commands
return 0 # Optional return value (0-255)
}
# Method 2
function function_name {
commands
}
Function Parameters
greet() {
echo "Hello, $1!" # $1 is first parameter
echo "Args: $@" # All parameters
echo "Count: $#" # Parameter count
}
greet "Alice" "Bob"
Return Values
# Return exit status (0-255)
is_even() {
[ $(($1 % 2)) -eq 0 ]
}
# Return value via echo
get_sum() {
echo $(($1 + $2))
}
# Usage
if is_even 4; then
echo "Even"
fi
result=$(get_sum 5 3)
Input/Output
Read Input
read var # Read into variable
read -p "Enter name: " name # With prompt
read -s password # Silent (for passwords)
read -t 5 var # Timeout after 5 seconds
read -n 1 char # Read single character
read -a array # Read into array
Output Redirection
command > file # Redirect stdout to file
command >> file # Append stdout to file
command 2> file # Redirect stderr to file
command 2>&1 # Redirect stderr to stdout
command &> file # Redirect both to file
command < file # Redirect stdin from file
command << EOF # Here document
text
EOF
command <<< "string" # Here string
File Descriptors
exec 3< input.txt # Open file for reading on FD 3
exec 4> output.txt # Open file for writing on FD 4
read -u 3 line # Read from FD 3
echo "text" >&4 # Write to FD 4
exec 3<&- # Close FD 3
File Operations
Basic File Commands
touch file.txt # Create empty file
mkdir -p dir/subdir # Create directory tree
cp -r source dest # Copy recursively
mv old new # Move/rename
rm -rf dir # Remove recursively
chmod 755 file # Change permissions
chown user:group file # Change ownership
File Manipulation
# Check if file exists
[ -f file.txt ] && echo "File exists"
# Create temp file
tempfile=$(mktemp)
trap "rm -f $tempfile" EXIT
# Find files
find /path -name "*.txt" -type f -mtime -7
# Archive
tar -czf archive.tar.gz directory/
tar -xzf archive.tar.gz
Process Management
Process Control
command & # Run in background
jobs # List background jobs
fg %1 # Bring job 1 to foreground
bg %1 # Resume job 1 in background
kill PID # Kill process
kill -9 PID # Force kill
killall process_name # Kill by name
Process Information
ps aux # List all processes
ps -ef # Full format listing
pgrep process_name # Get PID by name
pidof process_name # Get PID by exact name
Signals and Traps
# Trap signals
trap 'echo "Interrupted"' INT
trap 'cleanup' EXIT
trap '' TERM # Ignore SIGTERM
# Common signals
# INT (2) - Interrupt (Ctrl+C)
# TERM (15) - Terminate
# KILL (9) - Force kill
# HUP (1) - Hangup
Text Processing
grep - Search Text
grep pattern file # Search pattern in file
grep -i pattern file # Case insensitive
grep -r pattern dir/ # Recursive search
grep -v pattern file # Invert match
grep -n pattern file # Show line numbers
grep -E 'regex' file # Extended regex
sed - Stream Editor
sed 's/old/new/' file # Replace first occurrence
sed 's/old/new/g' file # Replace all occurrences
sed -i 's/old/new/g' file # In-place edit
sed -n '5p' file # Print line 5
sed '1,5d' file # Delete lines 1-5
sed '/pattern/d' file # Delete matching lines
awk - Text Processing
awk '{print $1}' file # Print first column
awk -F: '{print $1}' /etc/passwd # Custom delimiter
awk 'NR==5' file # Print line 5
awk '$3 > 100' file # Conditional print
awk '{sum+=$1} END {print sum}' file # Sum column
cut - Extract Columns
cut -d: -f1 /etc/passwd # Cut by delimiter
cut -c1-10 file # Cut characters 1-10
cut -f2,4 file # Cut fields 2 and 4
sort and uniq
sort file # Sort alphabetically
sort -n file # Sort numerically
sort -r file # Reverse sort
sort -k2 file # Sort by column 2
uniq file # Remove duplicates
uniq -c file # Count occurrences
sort file | uniq # Sort then deduplicate
System Administration
User Management
# Check current user
whoami
id
# Switch user
su - username
sudo command
# User info
getent passwd username
groups username
System Information
uname -a # System info
hostname # Hostname
uptime # System uptime
df -h # Disk usage
du -sh directory/ # Directory size
free -h # Memory usage
top # Process monitor
lscpu # CPU info
Network Commands
ifconfig # Network interfaces (old)
ip addr show # Network interfaces (new)
netstat -tuln # Network connections
ss -tuln # Socket statistics
ping -c 4 google.com # Test connectivity
curl -I https://example.com # HTTP headers
wget https://example.com/file # Download file
Service Management
# SystemD (modern)
systemctl start service
systemctl stop service
systemctl restart service
systemctl status service
systemctl enable service # Start at boot
# SysV (older)
service name start
/etc/init.d/service start
Log Management
tail -f /var/log/syslog # Follow log
journalctl -u service # SystemD logs
grep ERROR /var/log/app.log # Search logs
logrotate -f /etc/logrotate.conf # Force log rotation
Advanced Topics
Command Substitution
# Modern syntax
result=$(command)
result=$(command1 | command2)
# Old syntax (avoid)
result=`command`
Process Substitution
diff <(ls dir1) <(ls dir2) # Compare outputs
while read line; do
echo "$line"
done < <(command) # Read from command output
Arrays
# Indexed arrays
arr=(a b c)
arr[3]=d
echo ${arr[0]} # First element
echo ${arr[@]} # All elements
echo ${#arr[@]} # Array length
# Associative arrays
declare -A dict
dict[key1]=value1
dict[key2]=value2
echo ${dict[key1]}
echo ${!dict[@]} # All keys
Regular Expressions
# Basic regex matching
[[ "string" =~ regex ]]
# Example
if [[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
echo "Valid email"
fi
# Capture groups
if [[ "user123" =~ ([a-z]+)([0-9]+) ]]; then
echo "User: ${BASH_REMATCH[1]}"
echo "Number: ${BASH_REMATCH[2]}"
fi
Debugging
set -x # Print commands before execution
set -e # Exit on error
set -u # Exit on undefined variable
set -o pipefail # Exit on pipe failure
set -euo pipefail # Combine all
# Debug specific section
set -x
commands
set +x
Performance Tips
# Use built-in commands when possible
# Bad: cat file | grep pattern
# Good: grep pattern file
# Avoid useless use of cat
# Bad: cat file | command
# Good: command < file
# Use parameter expansion instead of external commands
# Bad: echo $string | cut -c1-5
# Good: echo ${string:0:5}
Best Practices
Error Handling
#!/bin/bash
set -euo pipefail
error_exit() {
echo "Error: $1" >&2
exit 1
}
# Check command existence
command -v git >/dev/null 2>&1 || error_exit "git not found"
# Check file existence
[ -f "config.txt" ] || error_exit "Config file not found"
Script Template
#!/bin/bash
#
# Script: script_name.sh
# Description: Brief description
# Author: Your Name
# Date: $(date +%Y-%m-%d)
#
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 this help message
-v, --verbose Enable verbose mode
EOF
}
# Main
main() {
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help) usage; exit 0 ;;
-v|--verbose) VERBOSE=1 ;;
*) echo "Unknown option: $1"; usage; exit 1 ;;
esac
shift
done
# Main logic here
}
# Run main function
main "$@"
Security Best Practices
# Quote variables
rm "$file" # Not rm $file
# Use -- to separate options from arguments
grep -- "$pattern" file
# Validate input
if [[ ! "$input" =~ ^[a-zA-Z0-9_-]+$ ]]; then
error_exit "Invalid input"
fi
# Use mktemp for temporary files
tempfile=$(mktemp)
trap 'rm -f "$tempfile"' EXIT
# Avoid eval
# Bad: eval "command $user_input"
# Good: Use arrays or proper quoting
Common Interview Scenarios
1. Monitor Disk Usage
#!/bin/bash
# Alert if disk usage exceeds threshold
THRESHOLD=80
df -h | awk 'NR>1 {gsub(/%/,"",$5); if($5 > '"$THRESHOLD"') print "Alert: "$1" is "$5"% full"}'
2. Log Rotation Script
#!/bin/bash
# Rotate logs when size exceeds limit
MAX_SIZE=1048576 # 1MB
LOG_FILE="/var/log/app.log"
if [ -f "$LOG_FILE" ] && [ $(stat -f%z "$LOG_FILE" 2>/dev/null || stat -c%s "$LOG_FILE") -gt $MAX_SIZE ]; then
mv "$LOG_FILE" "$LOG_FILE.$(date +%Y%m%d_%H%M%S)"
touch "$LOG_FILE"
fi
3. Backup Script
#!/bin/bash
# Daily backup with retention
BACKUP_DIR="/backup"
SOURCE_DIR="/data"
RETENTION_DAYS=7
# Create backup
tar -czf "$BACKUP_DIR/backup_$(date +%Y%m%d).tar.gz" "$SOURCE_DIR"
# Remove old backups
find "$BACKUP_DIR" -name "backup_*.tar.gz" -mtime +$RETENTION_DAYS -delete
4. Process Monitor
#!/bin/bash
# Restart service if not running
SERVICE="nginx"
if ! pgrep -x "$SERVICE" > /dev/null; then
echo "Starting $SERVICE..."
systemctl start "$SERVICE"
fi
5. User Creation Script
#!/bin/bash
# Bulk user creation from file
while IFS=: read -r username password; do
if ! id "$username" &>/dev/null; then
useradd -m "$username"
echo "$username:$password" | chpasswd
echo "Created user: $username"
fi
done < users.txt
6. System Health Check
#!/bin/bash
# Basic system health report
echo "=== System Health Report ==="
echo "Hostname: $(hostname)"
echo "Uptime: $(uptime -p)"
echo "Load Average: $(uptime | awk -F'load average:' '{print $2}')"
echo "Memory Usage: $(free -h | awk '/^Mem:/ {print $3"/"$2}')"
echo "Disk Usage:"
df -h | grep -E '^/dev/' | awk '{print $1": "$3"/"$2" ("$5")"}'
7. Find and Replace in Multiple Files
#!/bin/bash
# Replace text in all files recursively
find . -type f -name "*.txt" -exec sed -i 's/oldtext/newtext/g' {} +
8. Parse CSV File
#!/bin/bash
# Process CSV file
while IFS=, read -r col1 col2 col3; do
echo "Processing: $col1, $col2, $col3"
done < file.csv
9. Check Service Status Across Multiple Servers
#!/bin/bash
# Check nginx status on multiple servers
SERVERS=("server1" "server2" "server3")
SERVICE="nginx"
for server in "${SERVERS[@]}"; do
echo -n "$server: "
ssh "$server" "systemctl is-active $SERVICE" 2>/dev/null || echo "Failed"
done
10. Archive Old Files
#!/bin/bash
# Archive files older than 30 days
find /logs -name "*.log" -mtime +30 -exec tar -rf archive.tar {} \; -exec rm {} \;
gzip archive.tar
Quick Reference Card
Essential One-Liners
# Count lines in file
wc -l file.txt
# Find largest files
find . -type f -exec du -h {} + | sort -rh | head -10
# Monitor log in real-time
tail -f /var/log/syslog | grep ERROR
# Kill process by port
lsof -ti:8080 | xargs kill -9
# Check listening ports
netstat -tuln | grep LISTEN
# Find files modified in last 24h
find . -mtime -1 -type f
# Replace string in multiple files
find . -name "*.conf" -exec sed -i 's/old/new/g' {} +
# Check SSL certificate expiry
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates
# Generate random password
openssl rand -base64 32
# Quick backup with timestamp
tar -czf "backup_$(date +%Y%m%d_%H%M%S).tar.gz" /important/data
Common Pitfalls to Avoid
- Unquoted variables: Always quote
"$var" - Using backticks: Use
$(command)instead of`command` - Not checking exit codes: Use
set -eor check$? - Parsing ls output: Use glob patterns or find instead
- Not handling spaces in filenames: Use proper quoting and IFS
- Using cat unnecessarily: Many commands can read files directly
- Not validating user input: Always sanitize external input
- Forgetting to make scripts executable: Use
chmod +x
Interview Tips
- Know the difference between
[and[[ - Understand when to use
sourcevs executing a script - Be familiar with exit codes and their meanings
- Know how to handle signals and write trap handlers
- Understand the difference between
$@and$* - Be able to explain shell expansion order
- Know common performance optimizations
- Be prepared to debug scripts using
set -x
Remember: The best shell script is readable, maintainable, and handles errors gracefully. Always test your scripts thoroughly before using them in production.