Skip to content
Bash Shell Scripts — Complete Guide to Writing Reusable Automation

Bash Shell Scripts — Complete Guide to Writing Reusable Automation

DodaTech Updated Jun 4, 2026 10 min read

Bash scripts turn repetitive command-line tasks into reusable programs — combining variables, conditionals, loops, and functions in a text file that runs on any Unix system without extra software.

What You’ll Learn

  • Write your first Bash script with variables and command substitution
  • Use conditionals (if, elif, else) to make decisions
  • Automate repetitive work with for, while, and until loops
  • Organize logic into reusable functions with local variables
  • Handle errors with exit codes and set -e
  • Schedule scripts with cron

Why Shell Scripts Matter

Every repetitive task you do in the terminal — renaming files, checking disk space, backing up data — can be automated with a script. Durga Antivirus Pro uses Bash scripts to rotate scan logs, update virus definitions at 3 AM daily, and quarantine infected files automatically. DodaZIP scripts batch-compress uploads, verify archive integrity, and email reports. A single well-written script replaces hours of manual work every week.

Learning Path

    flowchart LR
  A[Bash Basics] --> B[Pipes & Redirection]
  B --> C[Shell Scripts<br/>You are here]
  C --> D[Permissions & Users]
  D --> E[System Monitoring]
  
Prerequisites: Familiarity with Bash (navigating files, running commands) and Linux like nano or vim. You should understand Bash.

What Is a Shell Script?

Think of a shell script as a recipe. A recipe lists ingredients (variables) and steps (commands). You write it once, and anyone with the same kitchen (a Unix system) can follow it to get the same result.

A script is just a text file with:

  1. A shebang line (#!/bin/bash) that says “use Bash to interpret this”
  2. Commands that run in order
  3. Execute permission so the system treats it as a program

Your First Script

#!/bin/bash
# My first script — says hello and shows the date
echo "Hello, World!"
echo "Today is: $(date)"

To run it:

# Make it executable (like turning on the "run" switch)
chmod +x hello.sh

# Execute it
./hello.sh
# Hello, World!
# Today is: Sat Jun 6 12:00:00 UTC 2026

Line by line:

  • #!/bin/bash — the shebang. It tells the kernel: “this script needs Bash.” Without it, the system tries to run the file with /bin/sh, which may not support all Bash features.
  • # — comments. Anything after # is ignored. Use comments to explain why you’re doing something.
  • echo — prints text to the terminal.
  • $(date)command substitution. Runs the date command and inserts its output. Think of it like: run this first, then use the result.

Variables — Labeled Boxes

A variable is a labeled box that holds a value. You put something in it and refer to it by the label.

#!/bin/bash

name="Alice"          # Put "Alice" into the box labeled "name"
greeting="Hello"      # Put "Hello" into the box labeled "greeting"

echo "$greeting, $name!"   # Take things out of the boxes
# Hello, Alice!

Key rules:

  • No spaces around =name = "Alice" is wrong (Bash thinks name is a command to run)
  • Use $ to get the value out — $name means “the contents of the box named name”
  • Quote variables with spaces — "$name" keeps the value intact

Command Substitution

# Run a command and store its output
files=$(ls -la)
echo "$files"

# Older syntax (same thing)
files=`ls -la`

Arithmetic

count=$((5 + 3))
echo "Count: $count"
# Count: 8

# Increment
count=$((count + 1))
echo "Now: $count"
# Now: 9

Special Variables

Bash automatically sets these variables:

VariableHoldsExample
$0Script name./backup.sh
$1, $2, …Positional arguments./script.sh hello$1 = hello
$#Number of arguments2 if you passed two args
$@All arguments as separate words"arg1" "arg2" "arg3"
$?Exit code of last command0 = success, 1 = error
$$Current script PID12345
$HOMEUser’s home directory/home/you
$USERCurrent usernameyou

Conditionals — Making Decisions

Conditionals let your script make choices. Think of them like a fork in the road.

#!/bin/bash

# If the first argument is "hello", greet warmly
if [ "$1" = "hello" ]; then
    echo "Hi there!"
elif [ "$1" = "bye" ]; then
    echo "See you!"
else
    echo "Say hello or bye"
fi

Breaking down the if syntax:

if [ condition ]; then
    commands
fi

The spaces inside [ ] are mandatory. [ "$1" = "hello" ] works; ["$1"="hello"] fails. Think of [ as a command itself — it needs spaces around its arguments.

File Tests

if [ -f "/etc/passwd" ]; then
    echo "passwd file exists"
fi

if [ -d "/home/you/projects" ]; then
    echo "Projects directory exists"
fi

Numeric Comparison

count=15
if [ "$count" -gt 10 ]; then
    echo "More than 10"
fi

Notice: -gt (greater than), not >. Bash uses letters for numeric comparison because > is used for redirection.

Logical Operators

if [ "$age" -gt 18 ] && [ "$age" -lt 65 ]; then
    echo "Working age"
fi

Loops — Doing Things Repeatedly

Loops are like an assembly line: the same operation repeats on each item.

For Loop

# Loop over a list of values
for fruit in apple banana cherry; do
    echo "I like $fruit"
done
# I like apple
# I like banana
# I like cherry
# Loop over files matching a pattern
for file in *.txt; do
    echo "Processing $file"
    wc -l "$file"
done
# C-style loop with a counter
for ((i=0; i<5; i++)); do
    echo "Iteration $i"
done

While Loop

# Loop while a condition is true
count=1
while [ "$count" -le 5 ]; do
    echo "Count: $count"
    count=$((count + 1))
done

Reading a File Line by Line

while IFS= read -r line; do
    echo "Line: $line"
done < input.txt

IFS= prevents splitting on spaces. -r prevents backslash interpretation. done < input.txt feeds the file as input.

Functions — Organizing Logic

Functions are like sub-recipes within your main recipe. They let you reuse logic without copying and pasting.

#!/bin/bash

# Define a function
greet() {
    local name="$1"       # $1 is the first argument passed to the function
    echo "Hello, $name!"
}

# Call it
greet "Alice"
greet "Bob"

Why local matters: Without local, variables inside functions are global. Modifying a global variable inside a function can cause bugs that are hard to track.

# Function with a return code
is_even() {
    local num=$1
    return $((num % 2))
}

if is_even 4; then
    echo "Even"
else
    echo "Odd"
fi

Exit Codes — Success or Failure

Every command returns an exit code: 0 means success, anything else means failure.

#!/bin/bash

# Check if a config file exists
if [ ! -f "config.yaml" ]; then
    echo "Error: config not found"
    exit 1
fi

echo "Config found, proceeding..."

The exit 1 stops the script immediately and tells the caller it failed. Think of it like: “Abort mission — missing equipment.”

Strict Mode — Write Safer Scripts

Always start your scripts with these three lines:

#!/bin/bash
set -e          # Exit immediately if any command fails
set -u          # Treat unset variables as an error
set -o pipefail # Make pipes fail if any part fails
  • set -e: Without this, your script cheerfully continues after rm nonexistent-file fails, potentially causing worse damage.
  • set -u: Catches typos. echo "$nme" (typo) exits immediately instead of printing an empty line.
  • set -o pipefail: Without it, false | echo "hi" exits with code 0 (success). With it, the pipe fails if any step fails.

Cron — Automate on a Schedule

Cron is like an alarm clock for your scripts. It runs them at specified times automatically.

# Edit your cron job schedule
crontab -e

# Syntax: minute hour day month weekday command
# ┌──────── minute (0-59)
# │ ┌────── hour (0-23)
# │ │ ┌──── day of month (1-31)
# │ │ │ ┌── month (1-12)
# │ │ │ │ ┌ weekday (0-7, 0=Sunday)
# │ │ │ │ │
# * * * * * command

# Run backup script daily at 3 AM
0 3 * * * /home/you/backup.sh

# Run health check every hour
0 * * * * /home/you/check-status.sh

# View scheduled jobs
crontab -l

Common Mistakes

1. Spaces around = in assignments

name = "Alice"   # WRONG — Bash thinks "name" is a command with args
name="Alice"     # CORRECT

2. Forgetting $ on variables

count=5
echo count       # Prints "count", not 5
echo $count      # CORRECT

3. Not quoting variables

file="My Document.txt"
cat $file        # Tries cat "My" and "Document.txt" — fails
cat "$file"      # CORRECT

4. Using [ instead of [[

[[ ]] has pattern matching, regex, and is safer with empty variables. Always prefer [[ ]] in Bash scripts.

5. Not using local in functions

Variables are global by default. Functions silently modify global state. Always declare function variables with local.

6. Forgetting set -e at the top

Without it, your script continues after errors, potentially corrupting data or causing cascading failures.

Practice Questions

  1. What does #!/bin/bash do? It’s the shebang — tells the system to use /bin/bash as the interpreter for the script.

  2. What is the difference between $@ and $*? $@ treats each argument as a separate word (preserves quoting). $* joins all arguments into one string. Always use $@.

  3. How do you debug a Bash script? Run bash -x script.sh to see every command executed, or add set -x inside the script to print commands as they run.

  4. What does set -e do? Makes the script exit immediately if any command fails (returns non-zero exit code).

  5. How do you read a file line by line in a script? while IFS= read -r line; do echo "$line"; done < file.txt

Challenge: Write a script that takes a directory path as an argument, checks if it exists, counts how many files are in it (recursively), and reports the total size in human-readable format. If the directory doesn’t exist, exit with code 1 and an error message.

FAQ

What is the difference between $@ and $*?
$@ treats each argument as a separate word, preserving any quoting. $* joins all arguments into a single string. Use $@ in loops and function calls.
How do I make a script executable?
chmod +x script.sh adds execute permission. Then run with ./script.sh. Without the ./, Bash looks in $PATH and won’t find your script.
What is cron and how do I use it?
A time-based job scheduler. Edit your schedule with crontab -e. Each line specifies when to run a script using minute, hour, day, month, weekday syntax.
How do I pass arguments to a script?
./script.sh arg1 arg2 arg3. Inside, $1 = arg1, $2 = arg2, $# = 3 (count), $@ = all args.
What is the difference between = and == in [[ ]]?
Both work for string comparison in [[ ]]. == is clearer for readability. In [ ], only = is POSIX-compliant.
How do I run a script every day at midnight?
Add 0 0 * * * /path/to/script.sh to your crontab. The five fields are minute, hour, day, month, weekday.

Try It Yourself

Create a simple backup script to practice:

#!/bin/bash
set -e

# Backup script — copies files and creates a timestamped archive
SOURCE="$HOME/Documents"
DEST="$HOME/backups"
DATE=$(date +%Y%m%d_%H%M%S)
FILENAME="backup-$DATE.tar.gz"

echo "Starting backup of $SOURCE..."

# Create destination if it doesn't exist
mkdir -p "$DEST"

# Create the archive
tar czf "$DEST/$FILENAME" "$SOURCE"

echo "Backup created: $DEST/$FILENAME"
echo "Size: $(du -sh "$DEST/$FILENAME" | cut -f1)"

Save as backup.sh, run chmod +x backup.sh, then ./backup.sh. Try breaking it (remove the set -e and see what happens when a command fails).

What’s Next

TutorialWhat You’ll Learn
Permissions & UsersManage read/write/execute permissions for scripts
System MonitoringMonitor CPU, memory, disk with scripts
Linux Cron JobsAdvanced scheduling and automation patterns
Python AutomationCompare Bash scripts with Python for complex tasks

What’s Next

Congratulations on completing this Bash Scripts tutorial! Here’s where to go from here:

  • Practice daily — Consistency is more important than long study sessions
  • Build a project — Apply what you learned by building something real
  • Explore related topics — Check out other tutorials in the same category
  • Join the community — Discuss with other learners and share your progress

Remember: every expert was once a beginner. Keep coding!

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro