How to Create a Bash File Restore Tool

How to Create a Bash File Restore Tool

How to Create a Bash File Restore Tool

Backups are only half the story. I learned that the hard way once — I had a solid backup routine running for months, but when I actually needed to restore a file, I realized I’d never actually tested how painful the restore process would be. It took me nearly an hour of digging through nested archive folders just to pull back a single config file. That experience is exactly why I built a dedicated Bash restore tool, separate from my backup script, so that pulling files back is just as easy as backing them up in the first place.

This article walks through building that restore tool, and it pairs naturally with the Bash File Backup Tool article in this series — I’d recommend reading both together.

What “Restore” Actually Means in This Context

A restore tool needs to answer three questions quickly:

  1. What backups do I have available?
  2. Which specific files or folders do I want back?
  3. Where should they go, and should anything be overwritten?

Let’s build a tool around exactly those three questions, assuming a backup structure of timestamped tar.gz archives (the kind our Backup Tool article produces).

Prerequisites

Step 1: Listing Available Backups

#!/usr/bin/env bash
set -euo pipefail

BACKUP_DIR="${1:-$HOME/backups}"

if [[ ! -d "$BACKUP_DIR" ]]; then
    echo "Error: backup directory '$BACKUP_DIR' not found."
    exit 1
fi

echo "Available backups in $BACKUP_DIR:"
echo "----------------------------------"

i=1
declare -A BACKUP_MAP
while IFS= read -r file; do
    BACKUP_MAP[$i]="$file"
    size=$(du -h "$file" | cut -f1)
    echo "$i) $(basename "$file")  ($size)"
    ((i++))
done < <(find "$BACKUP_DIR" -maxdepth 1 -iname "backup-*.tar.gz" | sort -r)

Explaining the Internals

Step 2: Letting the User Choose a Backup

read -rp "Select a backup to restore from (number): " CHOICE

SELECTED="${BACKUP_MAP[$CHOICE]:-}"

if [[ -z "$SELECTED" ]]; then
    echo "Invalid selection."
    exit 1
fi

echo "Selected: $(basename "$SELECTED")"

${BACKUP_MAP[$CHOICE]:-} looks up the chosen number in our associative array, defaulting to an empty string if the key doesn’t exist — this lets us cleanly detect invalid input without the script crashing on an unset variable (which set -u would otherwise catch as an error).

Step 3: Previewing Archive Contents Before Restoring

Never restore blind. Let’s list what’s inside before extracting anything:

echo "Contents of this backup:"
echo "-------------------------"
tar -tzf "$SELECTED" | head -n 20

TOTAL_FILES=$(tar -tzf "$SELECTED" | wc -l)
echo "... ($TOTAL_FILES total entries)"

tar -tzf lists (-t) the contents of a gzip-compressed (-z) archive (-f) without extracting anything, which is exactly what we want for a safe preview. head -n 20 keeps the initial listing manageable for large archives.

Step 4: Restoring Specific Files or the Whole Archive

read -rp "Restore (a)ll files or (s)pecific file? [a/s] " MODE
RESTORE_DIR="${2:-./restored}"
mkdir -p "$RESTORE_DIR"

if [[ "$MODE" == "s" ]]; then
    read -rp "Enter the exact path/filename to restore (from the list above): " TARGET_FILE
    tar -xzf "$SELECTED" -C "$RESTORE_DIR" "$TARGET_FILE"
    echo "Restored '$TARGET_FILE' to '$RESTORE_DIR'"
else
    tar -xzf "$SELECTED" -C "$RESTORE_DIR"
    echo "Restored all files to '$RESTORE_DIR'"
fi

What’s Happening

Step 5: Adding a Confirmation Step Before Overwriting

If the user does want to restore directly into the original location, we should double-check first:

read -rp "Restore directly to original location? This may overwrite existing files. [y/N] " CONFIRM

if [[ "$CONFIRM" =~ ^[Yy]$ ]]; then
    ORIGINAL_LOCATION="${3:-/}"
    tar -xzf "$SELECTED" -C "$ORIGINAL_LOCATION"
    echo "Restored directly to $ORIGINAL_LOCATION"
else
    echo "Skipped direct restore. Files remain in '$RESTORE_DIR' for manual review."
fi

This explicit y/N confirmation, defaulting to “no” via the regex check ^[Yy]$, is a small but important safeguard against destructive mistakes — a theme you’ll notice throughout every tool in this series.

Full Combined Script

#!/usr/bin/env bash
set -euo pipefail

BACKUP_DIR="${1:-$HOME/backups}"
RESTORE_DIR="${2:-./restored}"

if [[ ! -d "$BACKUP_DIR" ]]; then
    echo "Error: backup directory '$BACKUP_DIR' not found."
    exit 1
fi

echo "Available backups:"
i=1
declare -A BACKUP_MAP
while IFS= read -r file; do
    BACKUP_MAP[$i]="$file"
    size=$(du -h "$file" | cut -f1)
    echo "$i) $(basename "$file")  ($size)"
    ((i++))
done < <(find "$BACKUP_DIR" -maxdepth 1 -iname "backup-*.tar.gz" | sort -r)

read -rp "Select a backup (number): " CHOICE
SELECTED="${BACKUP_MAP[$CHOICE]:-}"

if [[ -z "$SELECTED" ]]; then
    echo "Invalid selection."
    exit 1
fi

echo "Preview of contents:"
tar -tzf "$SELECTED" | head -n 20
echo "... ($(tar -tzf "$SELECTED" | wc -l) total entries)"

mkdir -p "$RESTORE_DIR"
read -rp "Restore (a)ll or (s)pecific file? [a/s] " MODE

if [[ "$MODE" == "s" ]]; then
    read -rp "Enter exact filename/path to restore: " TARGET_FILE
    tar -xzf "$SELECTED" -C "$RESTORE_DIR" "$TARGET_FILE"
else
    tar -xzf "$SELECTED" -C "$RESTORE_DIR"
fi

echo "Restore complete. Files are in '$RESTORE_DIR'."

Real-World Use Cases

Automating Restore Testing

It’s genuinely valuable to periodically verify that your latest backup can actually be restored, without manual intervention:

#!/usr/bin/env bash
LATEST=$(find "$HOME/backups" -iname "backup-*.tar.gz" | sort -r | head -n 1)
TEST_DIR=$(mktemp -d)

tar -xzf "$LATEST" -C "$TEST_DIR"

if [[ -n "$(ls -A "$TEST_DIR")" ]]; then
    echo "OK: Latest backup restores successfully."
else
    echo "ALERT: Latest backup appears empty or failed to restore!" | mail -s "Backup Restore Test FAILED" you@example.com
fi

rm -rf "$TEST_DIR"

mktemp -d creates a temporary directory with a unique, collision-free name, which is the correct way to create scratch space in scripts rather than hardcoding a path like /tmp/test.

Security Considerations

Optimization Tips

Troubleshooting

Common Mistakes to Avoid

Frequently Asked Questions

How do I restore just one file without extracting the whole archive? Use tar -xzf archive.tar.gz -C destination/ path/inside/archive, exactly as shown in Step 4 — tar supports selective extraction natively.

What if my backups aren’t named with a consistent timestamp pattern? Adjust the find -iname pattern in Step 1 to match your actual naming convention, or switch the sort to use modification time via find ... -printf '%T@ %p\n' | sort -rn.

Can I restore from a backup stored on a remote server? Yes — first rsync or scp the archive locally (or mount the remote location), then run this restore tool against the local copy for simplicity and speed.

Summary

We built a Bash restore tool that lists available backups in a friendly numbered menu, lets you preview contents before extracting anything, supports both full and single-file restores, and includes a confirmation step before any potentially destructive direct restore. The key takeaway is that restore tooling deserves just as much care and testing as backup tooling — a backup you’ve never successfully restored from isn’t a backup you can actually trust.

References

Exit mobile version