🖥️ Automating the Basics: A Hands-On Intro to Bash Scripting

This guide walks you through what Bash scripting is, how it’s used, and when to graduate to a programming language like Python.

🖥️ Automating the Basics: A Hands-On Intro to Bash Scripting

Whether you're a homelabber, sysadmin, or just love the command line—you’re running Debian, Ubuntu, Fedora, Arch, or something minimal and headless—Bash scripting gives you the power to automate the command line and simplify daily tasks. And there's no better way to learn than firing up your box and getting your hands dirty. For this, I will be using a Raspberry Pi with Raspberry Pi OS, which is just based on the Debian Linux distribution (Ubuntu, Mint, Kali) for theRaspberry Pi.

💡
Note: This isn’t some expert guide — it’s just me scripting my way through a few small problems and learning in public. Decided to share what I’ve picked up along the way. If it helps you get started or sparks an idea, mission accomplished.

💡 What You’ll Learn

  • What Bash scripting is (and what it isn’t)
  • Why it's used in real-world environments
  • How to write and run your first script
  • Key Bash concepts: variables, loops, conditionals
  • Real-life examples for system admins and homelabbers
  • When to use Bash vs. a real programming language

🔍 What Is Bash Scripting?

Bash scripting is the practice of writing a sequence of shell commands into a plain-text file and executing them like a mini-program.

Instead of typing the same commands over and over, a Bash script lets you write them once and run them as often as you need—with zero extra keystrokes.

🧠 When Is Bash Scripting Actually Used?

Use CaseExample
🛠️ System MaintenanceAuto-updating, cleaning /tmp, checking disk usage
📡 Network AutomationRunning NMAP scans, rotating logs, restarting services
🔁 Repetitive TasksBacking up configs, converting files, syncing directories
🧪 DevOps/CI PipelinesPrepping build environments, starting containers

🧰 Getting Started (No GUI Needed)

All you need is:

  • Any Unix-like OS (Linux, Raspberry Pi, macOS, WSL)
  • Bash shell (default in most distros)
  • A terminal with vi (or your preferred text editor)
  • Curiosity + a goal

🛠️ Step 1: Create Your First Script File (We’re Using vim — But You Do You)

If you're more comfortable with something else like nano, or even emacs, go for it — the content stays the same.

Let’s make our first Bash script:

vim hello.sh

In vim:

  • Press i to enter INSERT mode
  • Type or paste the following:
#!/bin/bash
echo "Hello, world! This is your first script."
  • Press Esc, then type :wq and hit Enter to save and exit

🧠 Tip: If you’re using nano, just type:

nano hello.sh

And save with CTRL + O, EnterCTRL + X.


🔐 Step 2: Make It Executable

chmod +x hello.sh

Now your script has execute permission — just like a program.


🚀 Step 3: Run It

./hello.sh

You should see:

Hello, world! This is your first script.

If you’re using sudo, always run with ./:

sudo ./hello.sh

Otherwise, you’ll get a command not found error — because sudo doesn’t check the current directory by default.


🔎 Bash Breakdown: What’s Happening?

  • #!/bin/bash — Shebang line tells the system to use Bash
  • echo — Prints to the terminal
  • chmod +x — Gives the script execution rights
  • ./script.sh — Runs the script from the current directory

🧠 Understanding Core Bash Scripting Concepts

These three building blocks—variablesconditionals, and loops—make your scripts flexible, smart, and powerful. Let’s walk through each with examples and real-life logic.


🌀 1. Variables: Store and Reuse Values

variable in Bash is used to store data like text, numbers, or file paths — and reuse them later in your script.

📦 Syntax:

name="Bryan"
echo "Welcome, $name!"
Output: Welcome, Bryan!

🔍 Real Example:

Let’s say you regularly back up a folder. Instead of hardcoding the path each time, you can store it as a variable:

backup_path="/etc"
tar -czf backup.tar.gz $backup_path

Variables make your script:

  • Easier to read
  • Easier to maintain
  • More portable

🧪 2. Conditionals: Make Decisions

Conditionals allow your script to take different paths depending on whether a test passes or fails (true or false).

📦 Syntax:

if [ condition ]; then
  # commands if true
else
  # commands if false
fi

Example: Check if a file exists

if [ -f /etc/passwd ]; then
  echo "The file exists!"
else
  echo "No such file found."
fi
✔️ -f checks for a file
✔️ -d checks for a directory
✔️ -z checks if a string is empty
✔️ -n checks if a string is not empty

🔍 Real Example:

Let’s warn the user if disk space is getting low:

space_left=$(df / | grep / | awk '{print $5}' | sed 's/%//')

if [ "$space_left" -gt 80 ]; then
  echo "⚠️ Disk usage is high: ${space_left}%"
else
  echo "✅ Disk space is healthy."
fi

Conditionals let your script react to real-world states.


🔁 3. Loops: Do It Again and Again

loop repeats a command or group of commands. Perfect when you want to do something for every file, every user, every log, etc.

📦 for Loop Syntax:

for item in list; do
  # commands
done

Example: Loop over files

for file in *.log; do
  echo "Found file: $file"
done
This would echo the name of every .log file in the current directory.

🔍 Real Example: Loop through users

Want to print every user account on your system?

for user in $(cut -d: -f1 /etc/passwd); do
  echo "User account: $user"
done
✅ This script scans your system’s user database and prints each account. You’ll see built-in system users like daemonwww-datassh, and finally your own user at the end (like steeletekk).

🧠 This is a great way to:

  • Audit accounts
  • Prepare for adding/removing users
  • Learn how your Linux system manages identity

🔐 What’s /etc/passwd?

It’s not just for passwords (despite the name). It’s the user account database for the system.

When you run:

cat /etc/passwd
You see a whole wall of user accounts, even though you probably only created one for yourself (like steeletekk). So what are the rest?

🛠️ These are system and service accounts

Linux creates a bunch of special-purpose user accounts during installation — each one is used to run a particular service, process, or daemon. They are not meant for login. They often have:

  • No password
  • No real home directory
  • A “nologin” shell (to prevent login access)
  • System users are usually UID 0–999(while Human users usually start at UID 1000+)

So, the first field before the : is always the username — that’s why we cut -f1.

You can use loops to:

  • Process multiple files
  • Iterate over command output
  • Automate batch operations

🧰 Bringing It All Together: A Practical Script Example

Let’s wrap variables, conditionals, and loops into a real script that cleans up old log files. This version includes sudo, where needed for real-world usage.

Let’s say we want to scan a directory for log files (*.log) and delete the ones older than a certain number of days. We'll define that as a variable, check if files exist (conditionals), and loop through them all.

📄 Script: cleanup_logs.sh

#!/bin/bash

# 🔁 VARIABLE: Define target directory and age threshold
log_dir="/var/log"
days_old=7

echo "Looking for log files older than $days_old days in $log_dir..."

# ✅ CONDITIONAL: Check if directory exists
if [ ! -d "$log_dir" ]; then
  echo "Directory $log_dir does not exist. Exiting."
  exit 1
fi

# 🔁 LOOP: Iterate over matching old files
for file in $(find "$log_dir" -name "*.log" -type f -mtime +"$days_old"); do
  echo "Deleting: $file"
  rm -f "$file"
done

echo "✅ Log cleanup complete."

Pop that into a file named cleanup_logs.sh, make it executable and try it out

Since you're cleaning up files in /var/log, many of them are owned by root, so you'll need sudo to delete them.

💡 What’s Happening?

FeatureUsage in Script
Variablelog_dir and days_old store configurable values
ConditionalChecks if $log_dir exists before running cleanup
LoopGoes through each file returned by find

🔐 Real Use Cases for This Script

  • Run via cron weekly to manage disk usage
  • Adapt for .gz.tmp.bak, etc.
  • Use in Pi-hole or Docker volume logs cleanup
  • Extend to email you a summary if disk space is low

⚙️ Another Simple Real-World Script Example: System Update

I run regular updates using sudo apt update && sudo apt upgrade -y

What I could do is, add it to a file named update.sh :

#!/bin/bash

echo "Updating system..."
sudo apt update && sudo apt upgrade -y
echo "All done!"

Save it as update.sh, make it executable, and run it when needed. Or schedule it in a cron job to keep systems up to date.

steeletekk@LabPi:~ $ cat update.sh
#!/bin/bash

echo "Updating system..."
sudo apt update && sudo apt upgrade -y
echo "All done!"


steeletekk@LabPi:~ $ chmod +x update.sh



steeletekk@LabPi:~ $ ./update.sh
Updating system...
Hit:1 http://deb.debian.org/debian bookworm InRelease
Get:2 http://deb.debian.org/debian-security bookworm-security InRelease [48.0 kB]
Hit:3 http://deb.debian.org/debian bookworm-updates InRelease
Hit:4 http://archive.raspberrypi.com/debian bookworm InRelease
Fetched 48.0 kB in 11s (4,477 B/s)
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
All packages are up to date.
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
Calculating upgrade... Done
The following package was automatically installed and is no longer required:
  libcamera0.3
Use 'sudo apt autoremove' to remove it.
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
All done!
steeletekk@LabPi:~ $

🧠 Bash Scripting vs. Programming Languages

Here’s where many get confused — Bash feels like programming, but it’s not the same as writing in Python or Go.

🧰 Bash Scripting: Command-Line Automation

FeatureBash Scripting
Built ForAutomating terminal tasks
Great AtSystem maintenance, scripting CLI workflows
Weak AtComplex logic, large applications
SetupMinimal — just a terminal and bash
Real UseBackups, monitoring, file manipulation

Use Bash when:

  • Automating system commands
  • Managing configs or logs
  • Writing quick CLI glue logic

🧠 Programming Languages: Application Development

FeaturePython / Go / Rust / JS
Built ForComplex logic, apps, and services
Great AtAPIs, file parsing, data handling
Weak AtCLI-only workflows (can be overkill)
SetupRequires interpreters or compiled binaries
Real UseBuilding tools, services, dashboards

Use a full language when:

  • You need structured logic
  • You’re working with APIs or data
  • Your Bash script starts to hit 100+ lines

⚔️ Real-World Comparison: Bash vs. Python

TaskBashPython
Backing up /etc tar command shutil, os modules
Renaming files in a folder for file in *; do mv... os.rename()
Parsing JSON😬 Use jq or external tools✅ Built-in
Error handling & retries☠️ Messy if/else✅ Try/except blocks
Building CLI apps⚠️ Hacky menus✅ Use argparse, click, etc.

🔐 Best Practices

  • Use # comments to document scripts
  • Test scripts in a sandbox before production
  • Always use "quoted variables" to handle spaces
  • Exit intentionally (exit 0 or exit 1)
  • Modularize when possible (break into functions)

📚 Where to Go From Here

  • Build a disk usage checker
  • Monitor open ports with ss or netstat
  • Script log cleanup (find /var/log -name "*.log" -mtime +7 -delete)
  • Backup Pi-hole, Unbound, or config files with a nightly cron script

📘 Final Thoughts: A Little Scripting Goes a Long Way

Bash scripting isn’t about perfection — it’s about progress. You don’t need to write elegant, complex code. You just need to solve a problem and let the system handle the repetition.

With just a few core concepts — variablesconditionals, and loops — you can automate:

  • Cleanup jobs
  • System updates
  • Monitoring checks
  • Backups
  • And just about anything else you do more than once

You’ve seen real-world examples. You’ve learned what /etc/passwd is actually for. And you've built something that could run silently in the background of your system every day, doing your dirty work.

🚦What’s Next?

Maybe that’s it — maybe not.

If there’s interest (or if I just feel like it), we’ll explore Phase 2:

  • Passing arguments to your scripts
  • Logging actions
  • Scheduling tasks with cron
  • Adding interactivity and safety nets

But for now… just keep writing small scripts, running them, breaking them, and learning by doing.

Until next time — script responsibly.


☮️ OUT!
— Bryan