thelinuxvault guide

Bash Scripting 101: Automate Your Linux Tasks

In the world of Linux, repetitive tasks—like backing up files, cleaning system clutter, or managing users—can eat up valuable time. What if you could automate these tasks with a few lines of code? Enter **Bash scripting**. Bash (Bourne Again Shell) is the default command-line interpreter for most Linux distributions, and bash scripting lets you chain together Linux commands into reusable scripts. Whether you’re a system administrator, developer, or casual Linux user, mastering bash scripting will supercharge your productivity and make you a more efficient "Linux power user." This guide will take you from bash basics to writing practical, real-world scripts. By the end, you’ll be automating tasks like a pro!

Table of Contents

  1. What is Bash Scripting?
  2. Getting Started: Your First Bash Script
  3. Basic Syntax: Variables, Comments, and I/O
  4. Control Structures: If-Else and Loops
  5. Functions: Reusable Code Blocks
  6. Command Substitution: Capture Output of Commands
  7. Practical Examples: Real-World Scripts
  8. Debugging: Fixing Common Issues
  9. Best Practices for Bash Scripting
  10. Conclusion
  11. References

What is Bash Scripting?

Bash scripting is the art of writing text files containing a sequence of Linux commands that the Bash shell can execute. Think of it as a “recipe” for the shell: instead of typing commands one by one, you write them down in a script, and the shell runs them automatically.

Why Bash Scripting?

  • Automation: Replace repetitive manual tasks (e.g., daily backups, log rotation).
  • Consistency: Ensure tasks are executed the same way every time (no human error).
  • Efficiency: Save hours by automating multi-step workflows.
  • Accessibility: Bash is preinstalled on all Linux systems—no extra tools needed!

Getting Started: Your First Bash Script

Let’s dive in by writing a simple “Hello World” script. Follow these steps:

Step 1: Create the Script File

Open a terminal and use a text editor like nano or vim to create a new file (e.g., hello_world.sh). The .sh extension is conventional for bash scripts.

nano hello_world.sh

Step 2: Add the Shebang Line

The first line of every bash script should be the shebang (#!), which tells the system which interpreter to use. For bash, this is:

#!/bin/bash

Step 3: Write the Script Logic

Below the shebang, add a command to print “Hello, World!” using echo:

#!/bin/bash

# This is a comment: Print a greeting
echo "Hello, World!"

Step 4: Make the Script Executable

By default, the script file won’t have execute permissions. Fix this with chmod:

chmod +x hello_world.sh

Step 5: Run the Script

Execute the script using ./ (to specify the current directory):

./hello_world.sh

Output:

Hello, World!

Congratulations! You’ve written and run your first bash script.

Basic Syntax: Variables, Comments, and I/O

Let’s break down essential bash scripting syntax.

Comments

Use # to add comments (lines ignored by the shell). Comments explain why the code does something, not just what it does:

#!/bin/bash

# This script greets the user
echo "Welcome!"  # Print a welcome message

Variables

Variables store data (text, numbers, etc.). Rules for variables:

  • No spaces around = (e.g., name="Alice" is good; name = "Alice" is bad).
  • Use descriptive names (e.g., backup_dir instead of b).
  • Access variables with $var_name (e.g., echo $name).

Example:

#!/bin/bash

# Define variables
name="Alice"
age=30

# Print variables
echo "Name: $name"
echo "Age: $age"

Output:

Name: Alice
Age: 30

Input/Output (I/O)

  • Output: Use echo to print text to the terminal:

    echo "This is output"  # Basic output
    echo "Today is $(date)"  # Output with dynamic content (more on this later)
  • Input: Use read to get input from the user. The -p flag adds a prompt:

    #!/bin/bash
    
    # Get user input
    read -p "Enter your name: " name
    
    # Greet the user
    echo "Hello, $name!"

    Run it:

    ./greet.sh
    Enter your name: Bob
    Hello, Bob!

Control Structures: If-Else and Loops

Control structures let you make decisions and repeat actions.

If-Else Statements

Use if-else to run code based on conditions.

Syntax:

if [ condition ]; then
  # Code to run if condition is true
elif [ another_condition ]; then
  # Code to run if first condition is false, second is true
else
  # Code to run if all conditions are false
fi  # Ends the if statement

Common Conditions:

  • Numbers: -eq (equal), -ne (not equal), -lt (less than), -gt (greater than).
  • Strings: = (equal), != (not equal).
  • Files: -f "file.txt" (file exists), -d "dir" (directory exists).

Example: Check if a file exists

#!/bin/bash

filename="example.txt"

if [ -f "$filename" ]; then
  echo "$filename exists!"
else
  echo "$filename does NOT exist."
fi

Loops

Loops repeat code until a condition is met.

For Loop

Iterate over a list of items (e.g., numbers, filenames).

Syntax:

for item in list; do
  # Code to run for each item
done

Example: Print numbers 1 to 5

#!/bin/bash

for num in {1..5}; do
  echo "Number: $num"
done

Output:

Number: 1
Number: 2
Number: 3
Number: 4
Number: 5

While Loop

Run code while a condition is true.

Syntax:

while [ condition ]; do
  # Code to run
done

Example: Countdown from 5

#!/bin/bash

count=5
while [ $count -gt 0 ]; do
  echo "Countdown: $count"
  count=$((count - 1))  # Decrement count
  sleep 1  # Wait 1 second
done
echo "Blast off!"

Functions: Reusable Code Blocks

Functions let you group code into reusable blocks. Use them to avoid repeating code.

Syntax:

function_name() {
  # Code here
  # Access arguments with $1, $2, etc. (first, second argument)
}

Example: A greeting function

#!/bin/bash

# Define a function
greet() {
  local name=$1  # Local variable (only exists in the function)
  echo "Hello, $name! Welcome."
}

# Call the function with an argument
greet "Charlie"

Output:

Hello, Charlie! Welcome.

Command Substitution: Capture Output of Commands

Use command substitution to store the output of a command in a variable. Use $(command) (preferred) or `command` (legacy syntax).

Example: Get the current date

#!/bin/bash

current_date=$(date +"%Y-%m-%d")  # Format date as YYYY-MM-DD
echo "Today is $current_date"

Output:

Today is 2024-05-20

Another Example: Count files in a directory

file_count=$(ls | wc -l)
echo "There are $file_count files in this directory."

Practical Examples: Real-World Scripts

Let’s build scripts that solve actual problems.

Example 1: Automated Backup Script

This script backs up a directory to a .tar.gz file and logs the action.

#!/bin/bash
# Backup Script: Backs up a source directory to a target location

# Configuration
source_dir="/home/user/documents"  # Directory to back up
target_dir="/mnt/backup"           # Where to store the backup
backup_name="docs_backup_$(date +%Y%m%d_%H%M%S).tar.gz"  # Unique filename with timestamp

# Check if source directory exists
if [ ! -d "$source_dir" ]; then
  echo "Error: Source directory $source_dir does not exist."
  exit 1  # Exit with error code 1
fi

# Create backup using tar
echo "Creating backup: $target_dir/$backup_name"
tar -czf "$target_dir/$backup_name" "$source_dir"

# Check if backup succeeded
if [ $? -eq 0 ]; then  # $? is the exit code of the last command (0 = success)
  echo "Backup completed successfully!"
  # Log the backup to a file
  echo "$(date): Backup created: $target_dir/$backup_name" >> "$target_dir/backup_log.txt"
else
  echo "Error: Backup failed."
  exit 1
fi

Example 2: System Cleanup Script

Free up disk space by deleting temporary files and clearing caches.

#!/bin/bash
# System Cleanup Script: Removes temp files and clears caches

echo "Starting system cleanup..."

# Remove temporary files (use with caution!)
echo "Clearing /tmp directory..."
sudo rm -rf /tmp/*  # Deletes all files in /tmp (safe for most systems)

# Clear apt cache (Debian/Ubuntu)
echo "Clearing apt cache..."
sudo apt clean

# Clear user cache (e.g., browser cache)
echo "Clearing user cache..."
rm -rf ~/.cache/*

echo "Cleanup complete!"

Example 3: User Creation Script

Create a new system user with a home directory (useful for admins).

#!/bin/bash
# User Creation Script: Adds a new user with a home directory

read -p "Enter username: " username

# Check if user already exists
if id "$username" &>/dev/null; then  # Suppress output with &>/dev/null
  echo "Error: User $username already exists."
  exit 1
fi

# Create user with home directory and bash shell
sudo useradd -m -s /bin/bash "$username"  # -m = create home dir; -s = set shell

# Check if useradd succeeded
if [ $? -eq 0 ]; then
  echo "User $username created successfully!"
  echo "Setting password for $username..."
  sudo passwd "$username"  # Prompt to set password
else
  echo "Error: Failed to create user $username."
  exit 1
fi

Debugging: Fixing Common Issues

Even pros make mistakes! Here’s how to debug scripts:

Enable Debugging Mode

Add set -x at the top of your script to print each command before it runs (great for tracking execution flow):

#!/bin/bash
set -x  # Enable debugging

echo "Hello"
name="Debug"
echo "Name: $name"

Output will show commands with + prefixes:

+ echo Hello
Hello
+ name=Debug
+ echo Name: Debug
Name: Debug

Check Exit Codes

The $? variable holds the exit code of the last command (0 = success, non-zero = error). Use it to debug failures:

ls non_existent_file
echo "Exit code: $?"  # Output: Exit code: 2 (error)

Use shellcheck

shellcheck is a tool that flags syntax errors and bad practices. Install it with sudo apt install shellcheck (Debian/Ubuntu) or sudo dnf install shellcheck (Fedora).

Run it on your script:

shellcheck my_script.sh

Best Practices for Bash Scripting

Write clean, maintainable scripts with these tips:

  1. Use Descriptive Names: Name scripts like backup_docs.sh, not script1.sh.
  2. Add Comments: Explain why (not just what) the code does.
  3. Handle Errors: Use set -e to exit the script if any command fails:
    #!/bin/bash
    set -e  # Exit on error
  4. Quote Variables: Always quote variables (e.g., "$filename") to handle spaces in filenames:
    bad: rm $file  # Fails if $file has spaces
    good: rm "$file"  # Works with spaces
  5. Test Thoroughly: Run scripts with test data before deploying to production.
  6. Reuse Code with Functions: Avoid repeating code—use functions!

Conclusion

Bash scripting is a powerful tool for automating Linux tasks, saving time, and reducing errors. With the basics covered—variables, control structures, functions, and command substitution—you can now write scripts to handle backups, system maintenance, user management, and more.

The key to mastery is practice: start small (e.g., a script to organize downloads), then tackle bigger projects. As you learn, explore advanced topics like arrays, regular expressions, and integrating tools like awk or sed for text processing.

Happy scripting!

References