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.