What you will achieve
Write maintainable Bash scripts on Linux with strict error handling, sane quoting, and arguments — the baseline for cron and systemd oneshot services.
1) Shebang and strict mode
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
2) Variables and quoting
name="server1"
echo "Backup ${name} at $(date -Iseconds)"
paths=("/etc" "/var/www")
Always quote expansions: "$var" prevents word splitting.
3) Conditionals and loops
if [[ -f /etc/os-release ]]; then
source /etc/os-release
echo "Running on $ID"
fi
for dir in "${paths[@]}"; do
tar -czf "/backup/$(basename "$dir").tar.gz" "$dir"
done
4) Functions and exit codes
log() { echo "[$(date -Iseconds)] $*"; }
trap 'log "Failed at line $LINENO"' ERR
Verify
shellcheck script.sh
bash -n script.sh
5) Arguments and usage
usage() { echo "Usage: $0 <host>"; exit 1; }
[[ $# -eq 1 ]] || usage
host=$1
6) Reading files safely
while IFS= read -r line; do
printf '%s\n' "$line"
done < "$file"
7) Temporary files
tmp=$(mktemp) || exit 1
trap 'rm -f "$tmp"' EXIT
Testing scripts
Use shellcheck and run with bash -x script.sh during development. For production cron jobs, log stdout/stderr to journald or a rotated log file.
8) set -x only in debug
[[ "${DEBUG:-}" == 1 ]] && set -x
Prerequisites
bash 4+ (check bash --version). Executable bit (chmod +x script.sh). Editor and shellcheck optional but recommended. Know difference between #!/bin/bash and #!/bin/sh on Debian (dash).
Cron environment minimalism
Cron jobs should use absolute paths and set PATH explicitly — #!/usr/bin/env bash scripts fail in cron if they assume interactive shell PATH.
read -r safety
read -r line < file-r disables backslash escape eating — prevents subtle input corruption in scripts reading user data.
errexit pitfalls
set -e does not trigger in pipelines unless set -o pipefail — curl | grep can hide curl failure. Always combine both in production scripts. Subshells and if conditions exempt some failures — read bash manual before assuming errexit catches everything.
shellcheck in CI
Add shellcheck step to GitHub Actions or git pre-commit — catches unquoted vars before deploy script runs as root at 2am via cron.
log with timestamp function
log() { printf '[%s] %s\n' "$(date -Iseconds)" "$*"; }Structured logs from cron jobs grep easier than bare echo without context.
printf over echo
printf '%s\n' "$var" portable — echo -e behaviour varies between bash and dash. Debian /bin/sh is dash — scripts targeting sh must avoid bashisms or use bash shebang explicitly.