I'm writing a Bash script to create a full snapshot of several source directories in a timestamped backup directory. For now, I'm fine with hard-coded paths. The script uses rsync to copy each source, logs rsync's output, and then compares the copied directory with the source using diff.
I'm using `set -euo pipefail` because I want the process to stop when something goes wrong. One source path intentionally does not exist so I can test the error handling. Are there any logic or reliability issues I should fix? In particular, I'm unsure whether the source checks, rsync pipeline, and post-copy comparison behave correctly when a source is missing or the backup differs.
2 Answers
Be cautious with `set -euo pipefail` rather than assuming it gives perfect failure handling. Its behavior depends on context, especially in conditionals, pipelines, and command substitutions. Your script should explicitly handle expected nonzero statuses, such as a missing source or `diff` finding a difference.
It would also be useful to add an `ERR` trap that records the command and location when an unexpected failure occurs. Since the backup is important, consider checking the exit status from rsync and making sure the log directory exists before attempting to write the log. Rsync generally preserves file metadata well when used with archive mode, so it is a reasonable choice for this task.
There are two important issues. First, the shebang must be the very first line of the file. If it appears after a comment, it is treated as an ordinary comment, so the script may be launched by the wrong shell. That would break Bash arrays and `pipefail`.
Second, `diff -r` returns status 1 when it finds differences. With `set -e`, that is treated as a failure and the script exits immediately, so later source directories will not be backed up. If you want to report the mismatch and continue, handle the status explicitly, for example with `diff -r ... || echo "Mismatch found"`. If a mismatch should abort the backup, make that choice explicit and log the failure first.
Also, comparing a large tree with `diff` can be relatively slow. An alternative is a checksum-based rsync dry run, such as `rsync -aNi --checksum "$SRC/" "$DESTINATION/"`, which reports files that would need updating without modifying the destination.
That makes sense. The misplaced shebang was only an editing mistake, but I hadn’t realized that `diff` returning 1 would trigger `set -e`. I do want to detect an incomplete backup, although using rsync for the verification should be more practical for large directories.

I was intentionally aiming for an immediate stop if the backup could not be completed, but I’ll distinguish expected results like a comparison mismatch from actual command failures. I’ll also look into adding a failure trap for better diagnostics.