Bash scripting featured

Bash Scripting for DevOps: How to Write Your First Script

Have you ever stared at a terminal screen, watching lines of text scroll by, and wondered how engineers manage to automate the most complex tasks with just a few keystrokes? It often looks like magic to the uninitiated. But here is the secret: it isn’t magic. It is Bash Scripting.

When I first stepped into the world of DevOps, I wasted hours every day doing the same repetitive tasks. I would manually check logs, move files around, and configure settings one click at a time. It was tedious, error-prone, and frankly, a waste of time. That changed when a senior engineer sat me down and showed me how to write my first script. Suddenly, tasks that took me half a day were completed in seconds.

Today, I want to pass that knowledge on to you. We are going to strip away the complexity and learn Bash scripting from the ground up. No computer science degree is required—just a willingness to learn and a desire to make your computer do the heavy lifting for you.


What Exactly Are Shell and Bash?

Before we dive into writing code, we need to understand the environment we are working in. People often use terms like “Shell,” “Bash,” and “Terminal” interchangeably, but they are distinct things.

Think of your computer’s operating system as a car.

  • The GUI (Graphical User Interface): This is the steering wheel, the pedals, and the dashboard buttons. You click icons to open folders or start applications. It is user-friendly but can be slow for bulk operations.
  • The CLI (Command Line Interface): This is the internal wiring and the engine control unit. Instead of clicking, you type commands to tell the computer exactly what to do.

The Shell is the program that interprets these commands. It acts as a bridge between you and the operating system kernel. When you type a command, the shell interprets it and communicates with the OS to execute the task.

Bash, which stands for Bourne Again Shell, is the most popular implementation of the shell program. Just like Chrome is a browser (and Firefox is another), Bash is a specific flavor of Shell. It is powerful, widely used, and comes standard on most Linux distributions and macOS.

Why Do We Need Scripting?

Imagine you need to create 100 folders for a project. In the GUI, you would right-click, select “New Folder,” type a name, and repeat… 100 times.

In the CLI, you can do it with one line. But what if you have a complex workflow? For example, check for log files, find errors, count them, and email a report. Typing those commands one by one every single day is still tedious.

That is where Scripting comes in. A Bash script is simply a text file containing a sequence of commands. Instead of typing them manually, you save them in a file and execute that file. The computer reads the file and runs the commands in order, automating the entire process.


Setting Up Your Workspace

You do not need a high-end development machine to learn Bash. You likely already have the tools installed.

  • Linux: You are all set. Open your terminal, and Bash is likely your default shell.
  • macOS: macOS comes with a shell called Zsh as the default. However, Bash is usually installed too. Just open your terminal and type bash, and you will switch to the Bash environment.
  • Windows: Windows handles things differently. The best option today is WSL (Windows Subsystem for Linux). It installs a genuine Linux environment directly on Windows. It is officially supported by Microsoft and is superior to older tools like Cygwin.

Once you have your terminal open, you are ready to create your first script.


Your First Script: The “Hello World”

Let’s start with the tradition of all programming tutorials: the “Hello World” script.

Step 1: Create the file: Open your terminal and type: touch my_first_script.sh The .sh extension is a convention. It helps humans identify that this is a shell script. Linux doesn’t actually care about extensions; it looks at the file’s content and permissions to decide how to run it.

Step 2: Write the code: Open this file in a text editor. You can use Vim, Nano, or a code editor like VS Code. For beginners, VS Code is often easier due to syntax highlighting. Inside the file, type the following: #!/bin/bash echo "Hello, World! This is my first script."

Breaking it down:

  • #!/bin/bash: This is called the Shebang. It must be the very first line. It tells the system, “Hey, when you run this file, use the Bash interpreter to read it.” Without this, the system might try to run it using a different shell, which could cause errors.
  • echo: This is the command to print text to the screen.

Step 3: Execute the script:
If you try to run the script now by typing ./my_first_script.sh, you will likely get an error: Permission denied. This is a security feature. By default, Linux treats files as text, not executable programs. We need to give the file “execute” permission. We do this using the chmod command: chmod +x my_first_script.sh Now, run it: ./my_first_script.sh You should see your message printed on the screen. Congratulations! You just wrote a program.


A Real-World Scenario: The Log Analyzer

To make this guide truly practical, let’s solve a realistic problem. Imagine you are a DevOps engineer. You have a folder containing log files from various applications (e.g., application.log, system.log). Your job is to check these logs daily for errors and “fatal” issues.

Doing this manually would involve opening files, searching for the word “error,” counting them, and moving to the next file. Let’s automate this.

Step 1: Basic Commands to Logic

First, let’s understand the commands we need.

  • grep: This searches for text patterns inside files.
  • wc: This counts words or lines.

If we were doing this manually in the terminal, we might run:

grep "ERROR" application.log

This prints every line containing “ERROR”. To count them, we would pipe the output:

grep "ERROR" application.log | wc -l

This works for one file. But we want a script that handles everything automatically.

Step 2: Introducing Variables

In programming, we avoid “hard-coding” values. We don’t want to type “application.log” five times in our script. If the filename changes, we’d have to rewrite the whole script. Instead, we use Variables.

Variables in Bash are defined without spaces around the equals sign.

#!/bin/bash

LOG_DIR="/var/log/myapp"
FILE="application.log"
ERROR_PATTERN="ERROR"

echo "Analyzing $FILE..."
grep "$ERROR_PATTERN" "$LOG_DIR/$FILE"

Notice the syntax: LOG_DIR="/path". To use the value later, we prefix it with a dollar sign: $LOG_DIR.

Step 3: Lists and Arrays

What if we have multiple types of errors to check? We could write three separate blocks of code, but that is inefficient. Instead, we use an Array.

An array is a variable that holds multiple values.

ERROR_PATTERNS=("ERROR" "FATAL" "CRITICAL")

To access these values later, we need to know how arrays work in Bash. They are zero-indexed (the first item is at index 0).


Making Decisions: Control Flow

A script that just runs commands in a straight line is useful, but the real power comes from logic. We want our script to make decisions. “If the error count is high, alert me. Otherwise, just log it.”

Loops (The for Loop)

We have a directory of logs and a list of error types. We don’t want to write code for every combination. We want to say: “For every file in the folder, check for every error type.”

Here is how a loop looks in Bash:

for file in /var/log/myapp/*.log
do
    echo "Checking $file"
done

The system will look at the directory, find files ending in .log, and run the code between do and done for each one.

Conditionals (The if Statement)

Now, let’s add intelligence. We can count the errors and trigger an alert if the count exceeds a threshold.

COUNT=$(grep -c "ERROR" "$file")

if [ $COUNT -gt 10 ]; then
    echo "Warning! Too many errors in $file"
fi

Here, we are using -gt (greater than). Bash uses specific flags for comparison because > is used for file redirection.

OperatorMeaning
-eqEqual to
-neNot equal to
-gtGreater than
-ltLess than
-geGreater than or equal to

Visualizing the Logic

Sometimes, visualizing the flow helps understand the logic better. Here is a diagram of how our script will process the files.

Log Analysis Pipeline

Error Detection and Reporting Workflow

Terminal (Start/End)
Process
Decision

Building the Automation Script

Let’s put all these pieces together. We are going to build a script that:

  1. Defines the directory and error patterns.
  2. Finds files modified in the last 24 hours (so we don’t scan old logs).
  3. Loops through those files.
  4. Checks for specific error patterns.
  5. Alerts us if errors exceed a limit (e.g., 10 occurrences).

Here is the complete, optimized code. I have added comments (lines starting with #) to explain each part.

#!/bin/bash

# --- Configuration ---
# We use variables so we can easily change paths later
LOG_DIR="./logs"
REPORT_FILE="analysis_report.txt"
ERROR_PATTERNS=("ERROR" "FATAL" "CRITICAL")
THRESHOLD=10

# Clear the report file if it exists, or create a new one
> "$REPORT_FILE"

echo "Starting Log Analysis..."

# Find files modified in the last day (-mtime -1)
# We use command substitution $() to save the list to a variable
FILES=$(find "$LOG_DIR" -name "*.log" -mtime -1)

# Loop 1: Iterate through each file found
for FILE in $FILES; do
    echo "--------------------------------" >> "$REPORT_FILE"
    echo "Analyzing file: $FILE" >> "$REPORT_FILE"

    # Loop 2: Iterate through our error patterns
    # "${ARRAY[@]}" is the syntax to access all elements in the array
    for PATTERN in "${ERROR_PATTERNS[@]}"; do

        # Count occurrences of the pattern in the current file
        # grep -c returns the count directly
        COUNT=$(grep -c "$PATTERN" "$FILE")

        # Check if count is valid (grep returns exit code 1 if no match found)
        # We handle the case where the file might be empty or binary
        if [[ $COUNT -gt 0 ]]; then
            echo "Found $COUNT instances of $PATTERN" >> "$REPORT_FILE"
        fi

        # Conditional Logic: Alert if threshold is breached
        if [[ $COUNT -gt $THRESHOLD ]]; then
            echo "⚠️ ALERT: High number of $PATTERN detected in $FILE!" >> "$REPORT_FILE"
            # You could also send an email or Slack notification here
        fi
    done
done

echo "Analysis complete. Report saved to $REPORT_FILE."

Deep Dive: Understanding the Syntax

If you are new to Bash, some of the syntax above might look strange. Let’s break down the key parts that often confuse beginners.

The Space Sensitivity

In most programming languages, spaces are optional. In Bash, they are critical.

  • Variable Assignment: VAR="value" is correct. VAR = "value" will result in an error because Bash thinks VAR is a command to run.
  • Conditionals: if [ $COUNT -gt 10 ] is correct. The spaces inside the brackets [ ] are mandatory. Without them, the system cannot parse the logic.

Command Substitution $()

You will notice lines like FILES=$(find ...) and COUNT=$(grep ...).
This syntax runs the command inside the parentheses and captures the output into the variable. It is the modern, preferred way to do this (compared to the older backtick syntax `command`).

Appending to Files >> vs >

We used >> "$REPORT_FILE" in the loop.

  • > redirects output to a file and overwrites the file. We used this at the start (> "$REPORT_FILE") to clear the report before starting fresh.
  • >> redirects output and appends it to the end of the file. We used this inside the loops to add new data without deleting previous entries.

The Power of Automation: Beyond Log Analysis

The script we just built saves immense time. It turns a 30-minute manual check into a process that takes milliseconds. But the principles we learned—variables, loops, conditionals, and file redirection—apply to hundreds of other scenarios.

Here are a few other ways DevOps engineers and System Administrators use these exact skills:

  1. Automated Backups:
    A script can find files that have changed in the last day, compress them into a .tar.gz archive, and move them to a backup server. You can schedule this to run automatically every night using a tool called cron.
  2. Environment Setup:
    When a new developer joins a team, setting up a local environment can take days. A Bash script can install required software versions (like Node.js or Python), configure environment variables, and start databases with a single command. This ensures every developer has the exact same setup, avoiding “it works on my machine” issues.
  3. Cleaning Up Disk Space:
    Servers often crash because logs fill up the hard drive. A script can scan directories, find files older than 30 days, and safely delete them, emailing the admin a summary of what was cleaned.

Common Pitfalls and How to Avoid Them

Even experienced engineers make mistakes with Bash scripts. Here are three common pitfalls to watch out for:

  1. Permission Denied:
    As we saw earlier, scripts are just text files until you give them permission to execute. Always remember chmod +x scriptname.sh.
  2. Hardcoded Paths:
    Writing cd /home/user/documents/project inside a script makes it brittle. If you move the script to another server, it breaks. Always use variables for paths, or use relative paths (like ./logs) where possible.
  3. Handling Spaces in Filenames:
    Linux treats spaces as separators between arguments. If you have a file named My File.txt, a command like cat My File.txt will try to open two files: My and File.txt.
    The Fix: Always quote your variables. cat "$FILE" handles spaces correctly, whereas cat $FILE does not. This is why you see "$LOG_DIR" quoted in our examples.

Summary of Key Commands

To help you reference what we have learned, here is a table of the essential commands used in scripting.

CommandDescriptionExample
echoPrints text to the screen.echo "Hello World"
readTakes user input and saves it to a variable.read -p "Enter name: " NAME
touchCreates an empty file.touch newfile.txt
chmodChanges file permissions.chmod +x script.sh
grepSearches for text patterns.grep "error" logfile.txt
findSearches for files in a directory hierarchy.find /logs -name "*.log"
wcCounts lines, words, or bytes.wc -l filename.txt
mkdirCreates a directory.mkdir new_folder
test / [ ]Evaluates conditions.if [ -f file.txt ]; then ...

WarapUP

Bash scripting is not just a technical skill; it is a superpower for anyone working in a Linux environment. It allows you to delegate the boring, repetitive work to the computer so you can focus on solving actual problems.

We started with the basics of the shell and ended by building a fully functional tool that analyzes logs, counts errors, and alerts you to critical issues. The logic we used—variables to store data, loops to iterate, and conditionals to make decisions—is the foundation of all programming. Whether you are a DevOps engineer, a software developer, or a system administrator, mastering these concepts will make you significantly more efficient and valuable.

The best way to learn now is to break things. Take the script we wrote, modify it, break it, and fix it. Try automating a simple task on your own computer. Once you start automating, you will never want to go back to doing things manually.

References:

  1. GNU Bash Manual
  2. Advanced Bash-Scripting Guide

FAQs

What exactly is the difference between Bash and Shell?

Think of “Shell” as the generic term, like “Web Browser.” Just like Chrome and Firefox are both types of browsers, Bash is a specific type of Shell. The Shell is simply the program that listens to what you type in the terminal and talks to the computer to make things happen. Bash (Bourne Again Shell) is just the most popular version of that program found on Linux and macOS systems.

Why does my script say “Permission denied” when I try to run it?

This is the most common error for beginners. It happens because Linux treats your script file as just a text file by default, not as a program. For security reasons, it won’t let you run it until you give it specific permission. You can fix this instantly by running the command chmod +x yourscript.sh. This tells the computer, “It is safe to execute this file.”

Do I have to name my script with a .sh extension?

Technically, no. Linux does not look at the file name to decide how to run a program; it looks at the file permissions and content. You could name a script “myscript” or even “myscript.txt,” and it would still run if you have the correct permissions. However, adding .sh is a good habit because it helps you, the human user, quickly identify that the file contains a script.

What is that weird #!/bin/bash line at the top of scripts?

This line is called a Shebang. It acts like a signpost for your computer. When you run the script, the computer reads this line first to know which interpreter (program) it should use to read the rest of the file. If you leave it out, the computer might guess, or it might use a different shell (like sh or zsh) which could cause errors if you used specific Bash features.

Can I run Bash scripts on Windows?

Yes, but not directly in the old Command Prompt (cmd.exe). The best way to do it on modern Windows is by installing WSL (Windows Subsystem for Linux). This installs a real Linux environment inside your Windows operating system. Once WSL is set up, you can open a Linux terminal on Windows and run Bash scripts exactly as you would on a Linux server.

Why are spaces so important in Bash scripts?

Bash is very sensitive about spaces compared to other programming languages. For example, when writing a variable, you cannot put spaces around the equals sign.
Correct: name="John"
Incorrect: name = "John" If you add spaces, Bash thinks name is a command you are trying to run, and it will give you an error.

What is the main benefit of using a Bash script over just typing commands?

The biggest benefit is automation. If you need to check log files, back up data, and clean up folders every single day, typing those commands manually is a waste of time and prone to mistakes. A Bash script lets you save those instructions in a file. You run one command, and the script handles the rest perfectly every time.

How do I make my script check for errors automatically?

You use Conditional Statements (specifically if and else). Just like in English, you can tell the computer: “If the error count is greater than 10, send an alert. Otherwise, just print ‘Everything is fine’.” This adds logic to your script, allowing it to make decisions based on what it finds while running.

How can I save the output of my script to a file?

This is done using Redirection. Instead of printing results to the screen, you can send them to a text file using the > symbol.
./myscript.sh > results.txt (This saves the output).
If you want to add new results to the end of an existing file without deleting the old ones, use double arrows: ./myscript.sh >> results.txt.

Is Bash scripting still relevant with tools like Python and Ansible around?

Absolutely. While Python and Ansible are great for complex tasks, Bash is still the native language of the Linux operating system. It is lighter, faster, and available by default on almost every server without installing anything extra. For quick server maintenance, file manipulation, and system boot processes, Bash is still the industry standard.

Nishant G.

Nishant G.

Systems Engineer
Active since Apr 2024
246 Posts

A systems engineer focused on optimizing performance and maintaining reliable infrastructure. Specializes in solving complex technical challenges, implementing automation to improve efficiency, and building secure, scalable systems that support smooth and consistent operations.

You May Also Like

More From Author

4.7 3 votes
Would You Like to Rate US
Subscribe
Notify of
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments