Skip to content

Advanced Bash Footguns

Mistakes that cause outages, data loss, or silent corruption in production scripts.


1. No set -euo pipefail

Your script hits an error on line 12. Without strict mode, it silently continues and runs the destructive cleanup on line 40 with stale variables. The cleanup deletes the wrong directory because $TARGET was never set.

Fix: Always start with set -euo pipefail. Treat any script without it as untested.


2. Unquoted rm -rf $VARIABLE

The variable is empty. rm -rf $VAR/data becomes rm -rf /data. You just wiped a production directory. This has caused real outages at real companies.

Fix: Always quote: rm -rf "${VAR}/data". Better: validate the variable first: [[ -n "${VAR}" ]] || exit 1.


3. Parsing ls output

ls output is for humans, not scripts. Filenames with spaces, newlines, or special characters break every ls-based pipeline.

Fix: Use find with -print0 and xargs -0, or globbing with nullglob. Never pipe ls into while read or awk.


4. Using $? after an unrelated command

You check $? to see if the important command failed, but an echo or log call between the command and the check overwrites $? with its own exit code.

Fix: Capture immediately: mycommand; rc=$?; log "done"; if (( rc != 0 )); then .... Or just use if mycommand; then.


5. local masking exit codes

local result=$(failing_command) always returns 0 because local is the last command and it succeeded. The failure is silently swallowed.

Fix: Declare and assign separately: local result; result=$(failing_command).


6. Heredoc with unquoted delimiter expands variables

You SSH into a remote host with a heredoc. The $HOSTNAME in your remote script expands locally instead of on the remote machine. You're operating on the wrong host's data.

Fix: Quote the heredoc delimiter: ssh host bash <<'EOF' (not <<EOF).


7. Not handling SIGTERM/SIGINT

Your script creates temp files and modifies state. Someone hits Ctrl-C or the process gets killed. No cleanup runs. Lock files persist. Temp data leaks.

Fix: Always trap cleanup EXIT. The EXIT trap fires on normal exit, errors, and most signals.


8. Hardcoded paths instead of mktemp

Two users or two cron jobs run the same script simultaneously. They both write to /tmp/myscript.out. One overwrites the other's data mid-operation. Results are corrupted.

Fix: Use mktemp -d for unique directories. Never hardcode temp file paths.


9. Silent failures in SSH pipelines

ssh host 'command1 && command2' — if the SSH connection drops mid-command, the pipe returns the SSH error code, not the remote command's. You can't tell if command2 ran.

Fix: Use set -e on the remote side too. Log progress on the remote host. Use ssh -o ServerAliveInterval=15 to detect drops.


10. Testing in a different shell

You write and test in zsh or sh. The production server runs bash 4.2. Features like declare -A (associative arrays, bash 4.0), wait -n (bash 4.3), or ${var@Q} (bash 4.4) silently fail or behave differently.

Fix: Always use #!/usr/bin/env bash. Test on the target platform. Check ${BASH_VERSINFO[0]} if you need specific features.