Bash Scripting for Sysadmins: Practical Scripts You Will Actually Use
Bash scripting is the duct tape of system administration — it holds everything together, it is available on virtually every Unix-like system, and almost every sysadmin has written scripts they are not entirely proud of but which have been running uninterrupted for three years. This guide focuses on making those scripts better: more reliable, more readable, and less likely to cause 2 a.m. incidents.
The Header Every Script Needs
Start every serious Bash script with these three settings: set -e (exit immediately on any error), set -u (treat unset variables as errors), and set -o pipefail (pipe failures propagate). Without these, a script can silently continue after an error, use empty variables as if they were set, and succeed despite a failed command in a pipeline. These three lines alone prevent entire categories of subtle bugs. Add set -x during debugging to print each command before execution.
Argument Handling and Validation
Scripts that accept arguments should validate them: check that the expected number of arguments was passed (if [[ $# -ne 2 ]]; then), verify that required files or directories exist (if [[ ! -f "$1" ]]; then), and display a usage message when called incorrectly. Use getopts for option parsing with flags. Store arguments in named variables immediately — working with $1 and $2 throughout a script reduces readability dramatically.
"$variable", not $variable. Unquoted variables containing spaces, newlines, or special characters produce subtle bugs that only appear with real-world data.
Useful Patterns for Common Sysadmin Tasks
Log rotation and cleanup: find files older than N days and delete or archive them. Disk usage monitoring: check usage against a threshold and send an alert email if exceeded. Service health check: verify a service is running and restart it if not, with appropriate logging. Backup verification: confirm backup files exist, are recent, and are non-zero size. User account auditing: compare local accounts against an authoritative list and report discrepancies.
For each of these patterns, the implementation should include: error handling for each step, timestamped logging to a log file, idempotent design (safe to run multiple times), and a --dry-run option showing what would happen without making changes.
Testing Bash Scripts
Use shellcheck (shellcheck.net or the shellcheck package) for static analysis — it catches common bugs, style issues, and portability problems. For unit testing Bash scripts, the Bats (Bash Automated Testing System) framework provides a test structure similar to other unit testing tools. Test your scripts against edge cases: empty input, spaces in filenames, missing permissions, network failures, and disk full conditions. The cases that break scripts are always the ones not anticipated during initial development.
Read our introduction to Docker for beginners or explore our guides for more practical IT automation resources.