Blog » Linux » How to Write Bash Scripts in Linux: The Complete Beginner’s Guide
› how-to-write-bash-scripts

How to Write Bash Scripts in Linux: The Complete Beginner’s Guide

Table of Contents

If you’ve ever found yourself typing the same commands into your terminal over and over, you’re ready to learn how to write bash scripts. Trust me, I’ve been there. My first Arch install back in the day involved so much manual repetition that I swore I’d never type the same sequence twice again. Bash scripting changed everything for me, and it’ll do the same for you.

In this guide, I’ll walk you through everything you need to start writing your own scripts. We’ll cover the basics, dive into variables and loops, and I’ll share some hard-won lessons from years of breaking (and fixing) my own systems.

What is a Bash Script (And Why Every Linux User Should Learn to Write Them)

The Simple Definition: Automating Commands in a File

A bash script is just a text file containing shell commands. That’s it. When you run the script, bash executes each command in order, as if you typed them yourself. The magic is that you only write the commands once.

Think of it like a recipe. Instead of remembering every ingredient and step each time you cook, you write it down once. Then you just follow the recipe. Your script becomes the recipe for your computer.

Why Bash Scripts Matter for System Administration

Every sysadmin I know lives by one rule: automate everything. Scripts save time from the very first run. They eliminate human error. They ensure consistency across systems.

RackNerd Mobile Leaderboard Banner

Get a VPS from as low as $11/year! WOW!

Whether you’re backing up files, monitoring disk space, or managing users, a script handles it the same way every time. No typos. No forgotten steps. Just reliable execution.

My First Script Disaster (And What It Taught Me About Testing)

I need to tell you about the time I learned why testing matters. Early in my Linux journey, I wrote a cleanup script that was supposed to remove old log files. Instead, it wiped my entire home directory. I’d forgotten to quote a variable that contained a space, and bash interpreted it as separate arguments.

The command that ran wasn’t what I intended. I lost a weekend of work. But I gained a lesson I’ve never forgotten: always test scripts on sample data first. Always quote your variables. Always.

Setting Up Your First Bash Script

The Shebang Line: Why #!/bin/bash Matters

Every script starts with a shebang. This special line tells your system which interpreter to use:

#!/bin/bash

You might also see #!/usr/bin/env bash, which is more portable. It finds bash wherever it’s installed in your PATH. Either works, but env is safer across different Linux distributions.

Making Your Script Executable with chmod

After saving your script, you need to make it executable. This is where the chmod command comes in:

chmod +x myscript.sh

Now you can run it with ./myscript.sh. Without this step, you’d get a “permission denied” error.

Choosing the Right Text Editor

Use whatever editor you’re comfortable with. I use vim (yes, I’m one of those people), but nano works great for beginners. The editor doesn’t matter nearly as much as what you write in it.

Save your scripts with a .sh extension. It’s not required, but it makes your life easier.

Bash Script Basics: Variables and User Input

Creating and Using Variables

Variables store data you want to reuse. Here’s the critical syntax rule: no spaces around the equals sign.

# Correct
name="Alexa"

# Wrong - this will fail
name = "Alexa"

Access variables with a dollar sign: $name or ${name}. The curly braces version is safer when you’re combining variables with other text.

Always Quote Your Variables
Get in the habit of writing "${variable}" instead of just $variable. Unquoted variables cause word splitting and glob expansion, leading to unexpected behavior when values contain spaces or special characters.

Reading User Input with read Command

Interactive scripts often need user input:

#!/bin/bash
echo "What's your name?"
read username
echo "Hello, ${username}!"

The read command pauses and waits for input. Whatever the user types gets stored in the variable.

Command Substitution and Exit Codes

You can capture command output using $(command):

current_date=$(date +%Y-%m-%d)
echo "Today is ${current_date}"

Every command returns an exit code. Zero means success. Anything else means failure. Check it with $?:

grep "error" logfile.txt
if [[ $? -eq 0 ]]; then
    echo "Found errors in log"
fi

Understanding environment variables helps you build more powerful scripts that interact with your system.

Control Flow: Making Decisions in Your Scripts

If Statements and Conditional Testing

Scripts need to make decisions. The if statement handles this:

if [[ -f "/path/to/file" ]]; then
    echo "File exists"
else
    echo "File not found"
fi

Common test operators include:

  • -f: File exists and is regular file
  • -d: Directory exists
  • -z: String is empty
  • -n: String is not empty
  • -eq, -ne, -lt, -gt: Numeric comparisons

Using [[ ]] vs [ ] for Safer Conditionals

Always use double brackets [[ ]] instead of single brackets [ ]. Double brackets are bash-specific and handle edge cases better. They prevent word splitting inside the condition, so you won’t get errors when variables contain spaces.

Case Statements for Multiple Conditions

When you have many possible values, case statements are cleaner than long if-elif chains:

case "${input}" in
    start)
        echo "Starting service..."
        ;;
    stop)
        echo "Stopping service..."
        ;;
    *)
        echo "Unknown command"
        ;;
esac

Loops: Automating Repetitive Tasks

For Loops: Iterating Over Lists

For loops process items one at a time:

for server in web1 web2 web3; do
    echo "Pinging ${server}..."
    ping -c 1 "${server}"
done

While Loops: Conditional Repetition

While loops continue as long as a condition is true:

counter=1
while [[ ${counter} -le 5 ]]; do
    echo "Count: ${counter}"
    ((counter++))
done

Looping Through Files the Right Way

Here’s a beginner mistake I see constantly: using ls output in a loop. Don’t do it. Filenames with spaces break everything.

# Wrong - breaks on filenames with spaces
for file in $(ls *.txt); do
    echo "${file}"
done

# Correct - use globbing
for file in *.txt; do
    echo "${file}"
done

The grep command pairs well with loops for filtering and processing text files.

Functions: Writing Reusable Code

Defining and Calling Functions

Functions let you reuse code blocks:

greet_user() {
    echo "Hello, $1!"
}

greet_user "Alexa"
greet_user "Reader"

Function Parameters and Return Values

Access function arguments with $1, $2, etc. Use $@ for all arguments (always quoted: "$@").

Functions can return numeric exit codes with return, or output strings with echo that you capture with command substitution.

get_count() {
    local count=$(ls | wc -l)
    echo "${count}"
}

file_count=$(get_count)
echo "Found ${file_count} files"

Notice the local keyword. It keeps variables inside the function, preventing them from polluting your global namespace.

When to Use Functions vs Separate Scripts

If a code block appears more than once, make it a function. If it’s a standalone utility you’ll use across multiple scripts, make it a separate script.

Error Handling and Debugging

Using set -e and set -u for Safer Scripts

Production scripts need safety guards. Add these at the top:

#!/bin/bash
set -e  # Exit on error
set -u  # Treat unset variables as errors

With set -e, your script stops if any command fails. With set -u, referencing an undefined variable throws an error instead of silently becoming empty.

Redirecting Errors to stderr

Error messages belong on stderr, not stdout:

echo "Error: File not found" >&2
exit 1

This lets users redirect normal output and errors separately.

Debugging with set -x

When scripts misbehave, set -x shows each command before it runs:

#!/bin/bash
set -x
# Every command will be printed before execution

This saved me countless hours troubleshooting my homelab automation scripts.

Common Mistakes Beginners Make (And How to Avoid Them)

I’ve made all of these. Learn from my pain.

Not Quoting Variables

Unquoted variables cause word splitting. A filename like “my file.txt” becomes two arguments: “my” and “file.txt”. Always use "${variable}".

Ignoring Error Checking

Never assume commands succeed. Check exit codes, especially before destructive operations:

cd "/some/directory" || { echo "Failed to cd" >&2; exit 1; }
rm -rf ./*

Without that check, if cd fails, rm runs in the wrong directory. I don’t need to explain why that’s bad.

Using Unsafe Patterns

Never use rm -rf ${dir}/* without validating that ${dir} is set and correct. Check the common bash pitfalls reference for more examples of dangerous patterns.

Best Practices for Production-Ready Scripts

Using ShellCheck to Validate Your Code

ShellCheck is essential. It catches bugs before you run your script. Install it locally or paste your code on the website. I run it on every script before deployment.

“ShellCheck finds bugs in your shell scripts. It points out and clarifies typical beginner’s syntax issues, intermediate level semantic problems that cause a shell to behave strangely and counter-intuitively, and subtle caveats, corner cases and pitfalls.”

Adding Comments and Documentation

Future you will thank present you for comments:

#!/bin/bash
# Description: Backup user home directories
# Usage: ./backup.sh [username]
# Author: Alexa
# Last updated: 2025-09-15

Comment complex logic. Explain the “why” not just the “what.”

Structuring Scripts for Maintainability

Keep scripts focused. If a script exceeds 50 lines, consider breaking it into functions. Use meaningful variable names. Consistent indentation matters.

Tools like sed and awk become powerful allies when processing text in your scripts.

Real-World Bash Script Examples

Example 1: Automated Backup Script

#!/bin/bash
set -eu

backup_dir="/backups"
source_dir="${HOME}"
timestamp=$(date +%Y%m%d_%H%M%S)

mkdir -p "${backup_dir}"
tar -czf "${backup_dir}/home_${timestamp}.tar.gz" "${source_dir}" 2>/dev/null

echo "Backup created: ${backup_dir}/home_${timestamp}.tar.gz"

Example 2: System Monitoring Script

#!/bin/bash
set -eu

threshold=80
usage=$(df / | awk 'NR==2 {print $5}' | tr -d '%')

if [[ ${usage} -gt ${threshold} ]]; then
    echo "Warning: Disk usage at ${usage}%" >&2
    exit 1
fi

echo "Disk usage OK: ${usage}%"

Example 3: Batch File Renaming

#!/bin/bash
set -eu

for file in *.JPG; do
    [[ -f "${file}" ]] || continue
    newname="${file%.JPG}.jpg"
    mv "${file}" "${newname}"
    echo "Renamed: ${file} -> ${newname}"
done

Once your scripts are ready, scheduling scripts with cron lets them run automatically. Check checking system compatibility when writing scripts that need to work across different Linux distributions.

Quick Summary: Your Bash Script Starter Kit

  1. Start with #!/bin/bash
  2. Add set -eu for safety
  3. Quote all variables: "${var}"
  4. Use [[ ]] for conditionals
  5. Run ShellCheck before deploying
  6. Test on sample data first

Frequently Asked Questions

How long does it take to learn bash scripting?

You can write useful scripts within a week of practice. Mastery takes longer, but functional automation is quickly achievable. Start with simple tasks and build complexity gradually.

Should I learn bash or Python for automation?

Learn both. Bash excels at quick file operations and command chaining. Python handles complex logic and data processing better. For pure system administration, bash is essential.

Where should I save my scripts?

Personal scripts go in ~/bin (add it to your PATH). System-wide scripts belong in /usr/local/bin. Keep them organized and backed up.

Start Scripting Today

You now have everything you need to write your first bash script. Start simple. Automate one task you do repeatedly. Then another. Before long, you’ll wonder how you ever managed without scripts.

For deeper reference, the official Bash reference manual covers every edge case and feature.

Want to level up your Linux skills further? Check out our guides on automating tasks with cron or working with environment variables. Each builds on the scripting foundation you’ve started here.

Now go break something. Then fix it. That’s how you learn.

author avatar
Alexa Velinxs
I'm Alexa Velinxs, a cryptocurrency trading expert passionate about demystifying digital assets for both beginners and seasoned investors. Through my writing, I share actionable strategies, market insights, and practical tips to help you navigate the crypto landscape with confidence. Let's explore the future of finance together.
Related Posts