How can I simplify and improve my Bash backup script?

0
2
Asked By MellowCedar47 On

I'm fairly new to Bash scripting and am trying to tidy up a personal backup script. It creates date-stamped tar.xz archives from several folders, then transfers them to a mounted encrypted drive over the network using rsync. The script accepts up to four arguments, such as "essentials", "archive", and "paperwork". At the moment, it loops through the arguments and has a separate if statement for each one, with repeated tar, pv, xz, and rsync commands.

For example, one branch compresses a browser profile, shows progress with pv, writes the archive to /tmp, and then transfers it. Some source folders are in completely different locations, so I'm unsure whether putting the repeated work into a function would still be worthwhile.

I also want to improve error handling. I've previously checked commands using `$?`, but compression or transfer could fail at different stages, especially in a pipeline. I'd like the script to remain readable and make failures clear without burying everything in deeply nested if statements.

What would be a sensible Bash structure for this? Should I use a function for the common archive-and-transfer logic, a case statement for the arguments, and a different approach to checking pipeline errors?

4 Answers

Answered By SilverOtter6 On

You generally don’t need to inspect `$?` immediately after a command. In Bash, `if` directly tests the exit status of the command or pipeline it contains. For a successful archive followed by a successful transfer, you can chain them with `&&` or put the whole pipeline in an `if` statement.

Be careful with pipelines: by default, Bash reports the status of the last command in the pipeline. Enabling `set -o pipefail` makes the pipeline fail if an earlier command such as tar, pv, or xz fails. Then the function can return a nonzero status and the caller can decide how to report it.

Answered By GraniteFox31 On

A basic pattern would be something like:

`backup() {`
` local source=$1 label=$2`
` local file="${label}-${datestring}.tar.xz"`
` if tar -cf - -- "$source" | pv -s "$(du -sb -- "$source" | awk '{print $1}')" | xz -T0 > "/tmp/$file" && rsync -ah --progress "/tmp/$file" "$destination/$file"; then`
` printf '%sn' "$label backup complete"`
` else`
` printf 'Error backing up %sn' "$label" >&2`
` return 1`
` fi`
`}`

Then use `for arg; do case $arg in ... esac; done` to call it. In a real script, use `local` variables inside the function, quote paths, and consider cleaning up temporary archives afterward. You can also use `trap` for cleanup if the script is interrupted.

Answered By CopperLark82 On

Yes, a function is a good fit here because the archive and transfer steps are repeated. Pass the source directory and archive label as arguments, then keep the argument-specific part limited to selecting those values. A case statement is usually clearer than a long series of if statements.

Also, use a standard Bash shebang such as `#!/usr/bin/env bash`, run the script through ShellCheck, and quote variable expansions unless you have a specific reason not to. For command availability, `command -v pv` can be used to check whether a command is available; shell built-ins can be identified with `type` or `type -t`.

QuietMaple19 -

For example, the dispatch could look like `case $arg in essentials) backup "$HOME/.mozilla" firefox ;; archive) backup /some/path archive ;; *) echo "Unknown option: $arg" >&2; exit 2 ;; esac`. That keeps the folder-specific configuration separate from the common work.

Answered By AmberWillow54 On

For a personal script, simple nonzero exit codes and the existing diagnostics from tar, xz, and rsync may be enough. Extra error messages are useful when they add context, but checking every `$?` separately can make the script noisy without improving reliability.

You might also compare compression tools. xz can produce smaller files but is relatively slow; zstd or pigz may be faster, and rsync-friendly options can help when repeatedly transferring large archives. That’s an optimization to measure after the script structure is working.

Related Questions

LEAVE A REPLY

Please enter your comment!
Please enter your name here

This site uses Akismet to reduce spam. Learn how your comment data is processed.