norboten · cheat sheet
A script that cannot fail is not a backup — cheat sheet
bash, linux-basics
# quoting
"$var" # one argument, exactly
"$(cmd)" # the output as one argument (trailing newlines stripped)
"$@" # each positional argument preserved ← always this, never $@
"${arr[@]}" # each array element preserved
${var:-default} # this, or a default (works with set -u)
${var:?message} # …or die with a message if unset
# no quoting needed: inside [[ ]], and on the right of a plain x=$y assignment
# file lists, safely
tar -czf "$ARCHIVE" -C "$SRC" . # let the tool walk the tree ← preferred
find "$SRC" -type f -exec cmd {} + # batched, no shell in between
find "$SRC" -print0 | xargs -0 cmd # NUL-separated
find "$SRC" -print0 | while IFS= read -r -d '' f; do cmd "$f"; done
mapfile -d '' files < <(find "$SRC" -print0) # into an array (bash 4.4+)
# failing loudly
set -euo pipefail # -e stop on error, -u unset is an error, pipefail
log() { echo "$(date -Is) $*" >> "$LOG"; }
fail() { echo "$0: $*" >&2; log "FAILED: $*"; exit 1; }
[ -d "$SRC" ] || fail "no source $SRC" # -f file, -d dir, -r/-w/-x access
[ -n "$var" ] || fail "empty var" # -z empty, -n non-empty
cmd || fail "cmd failed"
if ! cmd; then fail "…"; fi
trap 'rm -rf "$tmp"' EXIT # clean up on any exit path
# debugging
bash -n script # syntax only, run nothing
bash -x script # print each command after expansion ← finds quoting bugs
set -x ; … ; set +x # trace one section
shellcheck script # static analysis; finds every unquoted expansion (not on the lab image: apt install shellcheck)
# cron
# min hour dom mon dow [user] command (user field: /etc/crontab, /etc/cron.d/* only)
15 2 * * * root /usr/local/bin/backup-data # absolute path — cron's PATH is /usr/bin:/bin
PATH=/usr/local/bin:/usr/bin:/bin # …or set it at the top of the crontab
# \% : a literal percent (unescaped % starts the stdin section)
crontab -l ; crontab -e ; crontab -u user -l
systemctl is-enabled cron ; systemctl is-active cron # Ubuntu
rc-update show default | grep crond ; rc-service crond status # Alpine
sudo env -i PATH=/usr/bin:/bin /usr/local/bin/job ; echo $? # reproduce cron's environment
journalctl -t CRON -b # what cron says it ran (systemd systems)
# exit 127 = command not found → PATH. exit 126 = found, not executable.