Linux Shell Scripting

Shell scripting is a powerful way to automate tasks, combine commands, and create custom tools in Linux. This guide covers bash scripting fundamentals, syntax, and practical examples for automation.

Overview

Shell scripting involves several key concepts:

  • Shell - Command interpreter (bash, sh, zsh, etc.)
  • Script - Text file containing shell commands
  • Shebang - First line specifying the interpreter (#!/bin/bash)
  • Variables - Store and manipulate data
  • Control Structures - Loops, conditionals, and functions
  • I/O Redirection - Handle input and output streams

Basic Script Structure

Simple script example

#!/bin/bash # This is a comment # Simple hello world script echo "Hello, World!" echo "Current date: $(date)" echo "Current user: $USER" echo "Current directory: $(pwd)"

Basic script structure with shebang, comments, and commands

Creating and running scripts

# Create script file nano hello.sh # Make executable chmod +x hello.sh # Run script ./hello.sh # Alternative ways to run bash hello.sh sh hello.sh

Steps to create, make executable, and run shell scripts

Variables

Variable declaration and usage

#!/bin/bash # Variable examples # String variables name="John Doe" greeting="Hello" # Numeric variables age=30 count=0 # Using variables echo "$greeting, $name!" echo "Age: $age" echo "Full message: ${greeting}, ${name}! You are ${age} years old." # Command substitution current_date=$(date) file_count=$(ls | wc -l) echo "Today is: $current_date" echo "Files in directory: $file_count"

Variable declaration, assignment, and usage in scripts

Special variables

#!/bin/bash # Special variables echo "Script name: $0" echo "First argument: $1" echo "Second argument: $2" echo "All arguments: $@" echo "Number of arguments: $#" echo "Process ID: $$" echo "Exit status of last command: $?" echo "Current user: $USER" echo "Home directory: $HOME"

Built-in variables available in shell scripts

Environment variables

# Set environment variable export MY_VAR="Hello World" # Use in script echo $MY_VAR # Check if variable is set if [ -z "$MY_VAR" ]; then echo "MY_VAR is not set" else echo "MY_VAR is set to: $MY_VAR" fi

Working with environment variables in scripts

Control Structures

If statements

#!/bin/bash # If statement examples age=25 # Simple if if [ $age -gt 18 ]; then echo "You are an adult" fi # If-else if [ $age -ge 21 ]; then echo "You can drink alcohol" else echo "You cannot drink alcohol" fi # If-elif-else if [ $age -lt 13 ]; then echo "You are a child" elif [ $age -lt 20 ]; then echo "You are a teenager" else echo "You are an adult" fi # String comparison name="Alice" if [ "$name" = "Alice" ]; then echo "Hello Alice!" fi # File tests if [ -f "/etc/passwd" ]; then echo "Password file exists" fi

Conditional statements and comparisons

Loops

#!/bin/bash # Loop examples # For loop with range for i in {1..5}; do echo "Number: $i" done # For loop with list for fruit in apple banana orange; do echo "Fruit: $fruit" done # For loop with files for file in *.txt; do echo "Processing: $file" done # While loop count=1 while [ $count -le 5 ]; do echo "Count: $count" count=$((count + 1)) done # Until loop num=1 until [ $num -gt 3 ]; do echo "Number: $num" num=$((num + 1)) done

Different types of loops for iteration

Case statements

#!/bin/bash # Case statement example echo "Enter your choice (1-3):" read choice case $choice in 1) echo "You chose option 1" ;; 2) echo "You chose option 2" ;; 3) echo "You chose option 3" ;; *) echo "Invalid choice" ;; esac # Case with patterns echo "Enter a file extension:" read ext case $ext in txt|doc) echo "Text document" ;; jpg|png|gif) echo "Image file" ;; mp3|wav) echo "Audio file" ;; *) echo "Unknown file type" ;; esac

Case statements for multiple condition handling

Functions

Function definition and usage

#!/bin/bash # Function examples # Simple function greet() { echo "Hello from function!" } # Function with parameters greet_user() { local name=$1 local age=$2 echo "Hello $name, you are $age years old" } # Function with return value add_numbers() { local num1=$1 local num2=$2 local result=$((num1 + num2)) echo $result } # Function that returns status check_file() { local filename=$1 if [ -f "$filename" ]; then return 0 # Success else return 1 # Failure fi } # Using functions greet greet_user "Alice" 25 sum=$(add_numbers 10 20) echo "Sum: $sum" if check_file "/etc/passwd"; then echo "File exists" else echo "File does not exist" fi

Creating and using functions in shell scripts

Operators and Comparisons

Type Operator Description Example
Numeric -eq Equal to [ $a -eq $b ]
-ne Not equal to [ $a -ne $b ]
-gt Greater than [ $a -gt $b ]
-lt Less than [ $a -lt $b ]
-ge Greater than or equal [ $a -ge $b ]
-le Less than or equal [ $a -le $b ]
String = Equal to [ "$a" = "$b" ]
!= Not equal to [ "$a" != "$b" ]
-z String is empty [ -z "$string" ]
File -f File exists and is regular file [ -f "$file" ]
-d Directory exists [ -d "$dir" ]
-r File is readable [ -r "$file" ]
-w File is writable [ -w "$file" ]

Input and Output

Reading user input

#!/bin/bash # Input examples # Simple input echo "Enter your name:" read name echo "Hello, $name!" # Input with prompt read -p "Enter your age: " age echo "You are $age years old" # Silent input (for passwords) read -s -p "Enter password: " password echo echo "Password entered" # Reading multiple values echo "Enter first and last name:" read first last echo "Hello, $first $last!" # Reading from file while read line; do echo "Line: $line" done < input.txt

Various ways to read user input and file content

Output and redirection

#!/bin/bash # Output examples # Standard output echo "This goes to stdout" # Error output echo "This is an error" >&2 # Redirect to file echo "This goes to file" > output.txt echo "This appends to file" >> output.txt # Redirect both stdout and stderr command > output.txt 2>&1 # Pipe to other commands echo "hello world" | tr '[:lower:]' '[:upper:]' # Here document cat << EOF This is a multi-line text block that will be processed by cat EOF

Output redirection and piping techniques

Error Handling

Exit codes and error checking

#!/bin/bash # Error handling examples # Check command success if cp source.txt destination.txt; then echo "File copied successfully" else echo "Failed to copy file" exit 1 fi # Check exit code explicitly cp source.txt destination.txt if [ $? -eq 0 ]; then echo "Copy successful" else echo "Copy failed" fi # Set error handling options set -e # Exit on any error set -u # Exit on undefined variable set -o pipefail # Exit on pipe failure # Trap errors trap 'echo "Error on line $LINENO"' ERR # Custom error function error_exit() { echo "Error: $1" >&2 exit 1 } # Usage [ -f "required_file.txt" ] || error_exit "Required file not found"

Proper error handling and debugging techniques

Practical Script Examples

System backup script

#!/bin/bash # System backup script BACKUP_DIR="/backup" SOURCE_DIR="/home" DATE=$(date +%Y%m%d_%H%M%S) BACKUP_FILE="backup_$DATE.tar.gz" # Create backup directory if it doesn't exist mkdir -p "$BACKUP_DIR" # Create backup echo "Starting backup..." if tar -czf "$BACKUP_DIR/$BACKUP_FILE" "$SOURCE_DIR"; then echo "Backup completed: $BACKUP_DIR/$BACKUP_FILE" # Remove old backups (keep last 7) find "$BACKUP_DIR" -name "backup_*.tar.gz" -mtime +7 -delete echo "Old backups cleaned up" else echo "Backup failed!" >&2 exit 1 fi

Automated backup script with error handling

Log monitoring script

#!/bin/bash # Log monitoring script LOG_FILE="/var/log/syslog" ALERT_EMAIL="[email protected]" ERROR_PATTERN="ERROR|CRITICAL|FATAL" # Check if log file exists if [ ! -f "$LOG_FILE" ]; then echo "Log file not found: $ LOG_FILE" exit 1 fi # Monitor for errors in last 100 lines recent_errors=$(tail -100 "$LOG_FILE" | grep -E "$ERROR_PATTERN") if [ -n "$recent_errors" ]; then echo "Errors found in log file:" echo "$recent_errors" # Send alert email (if mail is configured) echo "$recent_errors" | mail -s "System Alert: Errors Detected" "$ALERT_EMAIL" else echo "No recent errors found" fi

Log monitoring script with email alerts

System information script

#!/bin/bash # System information script echo "=== System Information Report ===" echo "Generated on: $(date)" echo echo "=== System Details ===" echo "Hostname: $(hostname)" echo "Uptime: $(uptime)" echo "Kernel: $(uname -r)" echo "Architecture: $(uname -m)" echo echo "=== CPU Information ===" grep "model name" /proc/cpuinfo | head -1 | cut -d: -f2 echo "CPU Cores: $(nproc)" echo echo "=== Memory Usage ===" free -h echo echo "=== Disk Usage ===" df -h | grep -E "^/dev" echo echo "=== Network Interfaces ===" ip addr show | grep -E "^[0-9]|inet " echo echo "=== Top 5 Processes by Memory ===" ps aux --sort=-%mem | head -6

Comprehensive system information gathering script

Advanced Scripting Techniques

Arrays

#!/bin/bash # Array examples # Declare array fruits=("apple" "banana" "orange" "grape") # Access elements echo "First fruit: ${fruits[0]}" echo "All fruits: ${fruits[@]}" echo "Number of fruits: ${#fruits[@]}" # Loop through array for fruit in "${fruits[@]}"; do echo "Fruit: $fruit" done # Add element fruits+=("mango") # Associative arrays (bash 4+) declare -A colors colors[red]="#FF0000" colors[green]="#00FF00" colors[blue]="#0000FF" echo "Red color code: ${colors[red]}" # Loop through associative array for color in "${!colors[@]}"; do echo "$color: ${colors[$color]}" done

Working with indexed and associative arrays

String manipulation

#!/bin/bash # String manipulation examples text="Hello World" # Length echo "Length: ${#text}" # Substring echo "Substring: ${text:0:5}" # "Hello" echo "From position: ${text:6}" # "World" # Replace echo "Replace: ${text/World/Universe}" # "Hello Universe" echo "Replace all: ${text//l/L}" # "HeLLo WorLd" # Case conversion echo "Uppercase: ${text^^}" echo "Lowercase: ${text,,}" # Remove prefix/suffix filename="document.txt" echo "Name without extension: ${filename%.*}" # "document" echo "Extension: ${filename##*.}" # "txt" path="/home/user/documents/file.txt" echo "Directory: ${path%/*}" # "/home/user/documents" echo "Filename: ${path##*/}" # "file.txt"

String manipulation and pattern matching

Command line argument processing

#!/bin/bash # Command line argument processing # Simple argument processing while [[ $# -gt 0 ]]; do case $1 in -h|--help) echo "Usage: $0 [options]" echo "Options:" echo " -h, --help Show help" echo " -v, --verbose Verbose output" echo " -f, --file Specify file" exit 0 ;; -v|--verbose) VERBOSE=1 shift ;; -f|--file) FILE="$2" shift 2 ;; *) echo "Unknown option: $1" exit 1 ;; esac done # Use the options if [[ $VERBOSE -eq 1 ]]; then echo "Verbose mode enabled" fi if [[ -n $FILE ]]; then echo "Processing file: $FILE" fi

Processing command line options and arguments

Shell Scripting Best Practices

Code Quality
  • Use meaningful variable and function names
  • Add comments to explain complex logic
  • Use consistent indentation (2 or 4 spaces)
  • Quote variables to prevent word splitting: "$variable"
  • Use set -e to exit on errors
  • Use set -u to catch undefined variables
Security Considerations
  • Validate all input parameters
  • Use absolute paths for critical commands
  • Avoid using eval with user input
  • Set proper file permissions (chmod 755 for scripts)
  • Don't store passwords in scripts
  • Use mktemp for temporary files
Common Pitfalls
  • Unquoted variables - Can cause word splitting and globbing
  • Missing error handling - Scripts should handle failures gracefully
  • Hardcoded paths - Use variables for paths that might change
  • Not checking prerequisites - Verify required commands exist
  • Race conditions - Be careful with file operations in concurrent environments

Debugging Shell Scripts

Debug options

# Run script with debug output bash -x script.sh # Enable debugging in script set -x # Enable debug mode set +x # Disable debug mode # Verbose mode set -v # Print commands as they are read # Combine options set -xv

Enable debugging and verbose output for troubleshooting

Debug techniques

#!/bin/bash # Debugging techniques # Debug function debug() { if [[ $DEBUG -eq 1 ]]; then echo "DEBUG: $*" >&2 fi } # Usage DEBUG=1 debug "Starting script execution" # Check variable values echo "Variable value: '$variable'" >&2 # Add checkpoints echo "Checkpoint 1: Before file processing" >&2 # Validate assumptions if [[ ! -f "$input_file" ]]; then echo "ERROR: Input file not found: $input_file" >&2 exit 1 fi # Log function entry/exit function_name() { echo "Entering function_name with args: $*" >&2 # function logic here echo "Exiting function_name" >&2 }

Debugging techniques and logging for script troubleshooting

Testing Shell Scripts

Basic testing approach

#!/bin/bash # Simple test framework test_count=0 pass_count=0 # Test function run_test() { local test_name="$1" local expected="$2" local actual="$3" test_count=$((test_count + 1)) if [[ "$expected" == "$actual" ]]; then echo "PASS: $test_name" pass_count=$((pass_count + 1)) else echo "FAIL: $test_name" echo " Expected: $expected" echo " Actual: $actual" fi } # Example tests source_script="./my_script.sh" # Test function output result=$(bash "$source_script" arg1 arg2) run_test "Basic functionality" "expected_output" "$result" # Test with different inputs result=$(bash "$source_script" --help) run_test "Help option" "Usage:" "${result:0:6}" # Summary echo "Tests: $test_count, Passed: $pass_count, Failed: $((test_count - pass_count))"

Simple testing framework for shell scripts

See also