Linux Shell Scripting Guide
Shell scripting is essential for automating tasks in Linux. This guide covers bash scripting fundamentals, best practices, and practical examples for system administration.
Script Basics
Creating a Shell Script
#!/bin/bash
# This is a comment
# Shebang line tells system which interpreter to use
echo "Hello, World!"
Making Scripts Executable
# Create script
nano myscript.sh
# Make executable
chmod +x myscript.sh
# Run script
./myscript.sh
# Or
bash myscript.sh
Variables
Variable Declaration and Usage
#!/bin/bash
# Variable assignment (no spaces around =)
NAME="John"
AGE=25
TODAY=$(date +%Y-%m-%d)
# Using variables
echo "Name: $NAME"
echo "Age: ${AGE}"
echo "Today is $TODAY"
# Read-only variable
readonly PI=3.14159
# Unset variable
unset NAME
Special Variables
#!/bin/bash
echo "Script name: $0"
echo "First argument: $1"
echo "Second argument: $2"
echo "All arguments: $@"
echo "Number of arguments: $#"
echo "Exit status of last command: $?"
echo "Process ID: $$"
echo "Current directory: $PWD"
echo "Home directory: $HOME"
echo "Current user: $USER"
Input and Output
Reading User Input
#!/bin/bash
# Basic input
echo "Enter your name:"
read name
echo "Hello, $name!"
# One-line prompt
read -p "Enter your age: " age
# Silent input (passwords)
read -sp "Enter password: " password
echo
# Read into array
read -a colors
echo "First color: ${colors[0]}"
# Read with timeout
read -t 5 -p "Quick! Enter something: " input
Command Line Arguments
#!/bin/bash
# Check number of arguments
if [ $# -eq 0 ]; then
echo "Usage: $0 <filename>"
exit 1
fi
# Use arguments
FILE=$1
OPTION=${2:-default} # Default value if not provided
echo "Processing file: $FILE"
echo "Option: $OPTION"
Conditionals
If Statements
#!/bin/bash
# Basic if
if [ $AGE -gt 18 ]; then
echo "Adult"
fi
# If-else
if [ $AGE -ge 18 ]; then
echo "Adult"
else
echo "Minor"
fi
# If-elif-else
if [ $AGE -lt 13 ]; then
echo "Child"
elif [ $AGE -lt 18 ]; then
echo "Teenager"
else
echo "Adult"
fi
# Multiple conditions
if [ $AGE -gt 18 ] && [ "$NAME" != "" ]; then
echo "Valid adult user"
fi
Test Operators
# Numeric comparisons
-eq # Equal
-ne # Not equal
-gt # Greater than
-ge # Greater than or equal
-lt # Less than
-le # Less than or equal
# String comparisons
= # Equal
!= # Not equal
-z # Empty string
-n # Not empty
# File tests
-e # Exists
-f # Is regular file
-d # Is directory
-r # Is readable
-w # Is writable
-x # Is executable
-s # Not empty
Case Statements
#!/bin/bash
read -p "Enter fruit: " fruit
case $fruit in
apple)
echo "Red or green"
;;
banana)
echo "Yellow"
;;
orange|lemon)
echo "Citrus fruit"
;;
*)
echo "Unknown fruit"
;;
esac
Loops
For Loops
#!/bin/bash
# Iterate over list
for item in apple banana orange; do
echo "Fruit: $item"
done
# Iterate over files
for file in *.txt; do
echo "Processing: $file"
done
# Range
for i in {1..10}; do
echo "Number:$i"
done
# C-style for loop
for ((i=0; i<10; i++)); do
echo "Count: $i"
done
# Array iteration
fruits=(apple banana orange)
for fruit in "${fruits[@]}"; do
echo "$fruit"
done
While Loops
#!/bin/bash
# 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 Loops
#!/bin/bash
count=0
until [ $count -ge 5 ]; do
echo "Count: $count"
((count++))
done
Functions
Defining Functions
#!/bin/bash
# Method 1
function greet() {
echo "Hello, $1!"
}
# Method 2
say_goodbye() {
echo "Goodbye, $1!"
}
# Call functions
greet "John"
say_goodbye "Jane"
Function with Return Value
#!/bin/bash
add_numbers() {
local num1=$1
local num2=$2
local sum=$((num1 + num2))
echo $sum
}
result=$(add_numbers 5 3)
echo "Sum: $result"
# Return status code
check_file() {
if [ -f "$1" ]; then
return 0
else
return 1
fi
}
if check_file "test.txt"; then
echo "File exists"
else
echo "File not found"
fi
Arrays
Array Operations
#!/bin/bash
# Declare array
fruits=("apple" "banana" "orange")
# Access elements
echo "${fruits[0]}" # First element
echo "${fruits[@]}" # All elements
echo "${#fruits[@]}" # Array length
# Add element
fruits+=("grape")
# Iterate array
for fruit in "${fruits[@]}"; do
echo "$fruit"
done
# Associative array (dictionary)
declare -A colors
colors[apple]="red"
colors[banana]="yellow"
echo "${colors[apple]}" # red
# Iterate associative array
for key in "${!colors[@]}"; do
echo "$key: ${colors[$key]}"
done
String Manipulation
#!/bin/bash
text="Hello, World!"
# Length
echo ${#text}
# Substring
echo ${text:7:5} # "World"
# Replace
echo ${text/World/Bash} # "Hello, Bash!"
# Uppercase/Lowercase
echo ${text^^} # HELLO, WORLD!
echo ${text,,} # hello, world!
# Remove prefix/suffix
file="document.txt"
echo ${file%.txt} # "document"
echo ${file#doc} # "ument.txt"
File Operations in Scripts
Reading Files
#!/bin/bash
# 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
Writing Files
#!/bin/bash
# Overwrite file
echo "New content" > file.txt
# Append to file
echo "Additional content" >> file.txt
# Here document
cat > config.txt << EOF
Setting1=value1
Setting2=value2
EOF
Error Handling
Exit Status
#!/bin/bash
# Check last command status
ls /nonexistent 2>/dev/null
if [ $? -ne 0 ]; then
echo "Command failed"
fi
# Exit with status code
if [ ! -f "$FILE" ]; then
echo "Error: File not found"
exit 1
fi
Error Handling Patterns
#!/bin/bash
# Exit on error
set -e
# Exit on undefined variable
set -u
# Exit on pipe failure
set -o pipefail
# Trap errors
trap 'echo "Error on line $LINENO"' ERR
# Cleanup on exit
trap 'rm -f /tmp/tempfile' EXIT
Practical Examples
Backup Script
#!/bin/bash
# Backup script
SOURCE="/var/www"
DEST="/backup"
DATE=$(date +%Y%m%d-%H%M%S)
FILENAME="backup-$DATE.tar.gz"
# Create backup
tar -czf "$DEST/$FILENAME" "$SOURCE"
# Check if successful
if [ $? -eq 0 ]; then
echo "Backup successful: $FILENAME"
# Clean old backups (keep last 7 days)
find "$DEST" -name "backup-*.tar.gz" -mtime +7 -delete
else
echo "Backup failed!" >&2
exit 1
fi
System Monitor
#!/bin/bash
# System monitoring script
while true; do
clear
echo "=== System Monitor ==="
echo "Time: $(date)"
echo ""
echo "CPU Usage:"
top -bn1 | grep "Cpu(s)" | awk '{print $2 "%"}'
echo "Memory Usage:"
free -h | awk '/^Mem:/ {print $3 "/" $2}'
echo "Disk Usage:"
df -h | grep "^/dev" | awk '{print $1 ": " $5}'
sleep 5
done
User Management Script
#!/bin/bash
# User management script
create_user() {
local username=$1
local fullname=$2
# Check if user exists
if id "$username" &>/dev/null; then
echo "User $username already exists"
return 1
fi
# Create user
sudo useradd -m -c "$fullname" "$username"
# Set password
echo "$username:temp123" | sudo chpasswd
# Force password change
sudo chage -d 0 "$username"
echo "User $username created successfully"
}
# Usage
create_user "jdoe" "John Doe"
Best Practices
Script Template
#!/bin/bash
#
# Script Name: myscript.sh
# Description: Brief description of what the script does
# Author: Your Name
# Date: 2025-02-07
# Version: 1.0
#
# Strict mode
set -euo pipefail
# Constants
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly SCRIPT_NAME="$(basename "$0")"
# Functions
usage() {
cat << EOF
Usage: $SCRIPT_NAME [OPTIONS] <argument>
Description of the script.
OPTIONS:
-h, --help Show this help message
-v, --verbose Enable verbose mode
-d, --debug Enable debug mode
EXAMPLES:
$SCRIPT_NAME file.txt
$SCRIPT_NAME -v file.txt
EOF
}
main() {
# Main script logic here
echo "Script running..."
}
# Run main function
main "$@"
Coding Standards
# Use meaningful variable names
user_name="john" # Good
x="john" # Bad
# Quote variables
rm "$file" # Good
rm $file # Bad (fails with spaces)
# Use [[ ]] instead of [ ]
if [[ "$var" == "value" ]]; then # Good
echo "Match"
fi
# Use lowercase for local variables
local_var="value"
# Use UPPERCASE for constants
readonly MAX_USERS=100
# Check if variable is set
if [[ -n "${VAR:-}" ]]; then
echo "VAR is set"
fi
Debugging Scripts
Debug Techniques
# Debug mode (print each command)
bash -x script.sh
# Or in script
set -x
# Specific section
set -x
# Debug this section
set +x
# Verbose mode
set -v
# Check syntax without executing
bash -n script.sh
# Print to stderr
echo "Error message" >&2
# Custom debug function
DEBUG=true
debug() {
if [[ "$DEBUG" == "true" ]]; then
echo "[DEBUG] $*" >&2
fi
}
debug "This is a debug message"
Quick Reference
# Variables
var="value" # Assign
echo "$var" # Use variable
readonly var="value" # Constant
# Conditionals
if [[ condition ]]; then
# commands
fi
# Loops
for item in list; do
# commands
done
while [[ condition ]]; do
# commands
done
# Functions
function_name() {
# commands
}
# Arrays
arr=(item1 item2)
echo "${arr[0]}" # Access
echo "${arr[@]}" # All items
echo "${#arr[@]}" # Length
# String operations
${#var} # Length
${var:offset:length} # Substring
${var//find/replace} # Replace
# File tests
-e file # Exists
-f file # Is file
-d file # Is directory
-r file # Readable