Linux Automation: Complete Guide to Bash Scripting
Bash scripting is the superpower of Linux system administration. Any task you do more than once should be automated with a script. Backups, file organization, system monitoring, server provisioning - all of these are faster and more reliable with Bash.
Bash is not the prettiest language. Its syntax is cryptic compared to Python. But it is the native language of Linux. It has zero dependencies, runs everywhere, and can control every aspect of the system. Every programmer who works with Linux should know enough Bash to be dangerous.
Bash Scripting Basics
A Bash script starts with a shebang: #!/bin/bash. This tells the system which interpreter to use. Make the script executable with chmod +x script.sh. Run it with ./script.sh. Always include the shebang and set -e at the top to exit on errors.
Variables are assigned without spaces: NAME="John". Use $NAME or ${NAME} to reference. Quoting matters: "$NAME" preserves spaces, '$NAME' treats it as literal text. Use export to make variables available to child processes. Environment variables like $HOME, $PATH, and $USER are always available.
Command substitution runs a command and captures its output. Two syntaxes: $(command) or `command`. The $() syntax is preferred because it nests better. Example: FILES=$(ls *.txt). Always quote command substitutions to preserve whitespace: FILES="$(ls)".
Conditionals and Loops
The if statement in Bash uses square brackets: if [ "$NAME" = "John" ]; then. The spaces around brackets are mandatory. Use = for string comparison, -eq for numeric, -f for file exists, -d for directory exists, -z for empty string. Use else and elif for multiple conditions.
For loops iterate over items: for file in *.txt; do echo "Processing $file"; done. While loops run while a condition is true: while [ -f /tmp/lock ]; do sleep 1; done. The until loop runs until a condition becomes true. Use break to exit early and continue to skip to the next iteration.
Case statements replace long if-elif chains: case "$1" in start) echo "Starting";; stop) echo "Stopping";; *) echo "Unknown";; esac. Each pattern ends with ), commands end with ;;, and *) is the default case.
Functions
Functions group reusable code: function log() { echo "[$(date)] $1"; }. Or without the function keyword: log() { ... }. Call functions like commands: log "Backup started". Local variables inside functions use local: local COUNTER=0. Functions can return values with echo (capture with $()) or with return (exit status 0-255).
Practical Automation Scripts
Backup script: tar -czf backup-$(date +%Y%m%d).tar.gz /home/user/documents. Log rotation: find /var/log -name "*.log" -mtime +30 -delete. System monitoring: check disk usage with df -h and alert if over 90%. User management: create users from a CSV file with a loop that calls useradd and sets passwords.
Always validate inputs in your scripts. Check that files exist before reading them. Check that commands succeed before proceeding. Use trap for cleanup: trap 'rm -f /tmp/tempfile' EXIT. This ensures temporary files are deleted even if the script crashes.