How to Create a Bash File Decompression Tool

How to Create a Bash File Decompression Tool

How to Create a Bash File Decompression Tool

I can’t count how many times I’ve downloaded an archive and had to stop and think, “wait, is this a .tar.gz, a .zip, or a .7z?” before I could even extract it. Every compression format has its own extraction command and its own flags, and remembering all of them is a waste of brainpower. So I built a universal Bash decompression tool that detects the archive type automatically and extracts it with the right command. In this article, I’ll show you exactly how I built it, piece by piece.

Why a Universal Decompression Tool Is Worth Building

Here’s the problem in plain terms. Extracting a .tar.gz requires tar -xzf. A .zip requires unzip. A .7z requires 7z x. A .rar requires unrar x. A .bz2 requires bzip2 -d. Nobody wants to memorize all of that, and it’s exactly the kind of repetitive task Bash scripting was made for.

Prerequisites

Make sure these utilities are installed (most distros have some by default):

sudo apt install tar gzip bzip2 xz-utils zip unzip p7zip-full unrar

On macOS, use Homebrew:

brew install p7zip unrar xz

Step 1: Detecting Archive Type

The most reliable way to detect a file’s real type isn’t the extension — it’s the file’s magic bytes, which the file command reads for us. Extensions can lie (someone might rename archive.zip to archive.txt), but magic bytes don’t.

file --mime-type -b myarchive.tar.gz

This outputs something like application/gzip. We’ll use this as our detection method, with the file extension as a fallback.

Step 2: Writing the Core Script

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

ARCHIVE="${1:-}"
DEST="${2:-.}"

if [[ -z "$ARCHIVE" ]]; then
    echo "Usage: $0 <archive-file> [destination-directory]"
    exit 1
fi

if [[ ! -f "$ARCHIVE" ]]; then
    echo "Error: '$ARCHIVE' not found."
    exit 1
fi

mkdir -p "$DEST"

MIME=$(file --mime-type -b "$ARCHIVE")

echo "Detected type: $MIME"

case "$MIME" in
    application/gzip|application/x-gzip)
        tar -xzf "$ARCHIVE" -C "$DEST"
        ;;
    application/x-bzip2)
        tar -xjf "$ARCHIVE" -C "$DEST"
        ;;
    application/x-xz)
        tar -xJf "$ARCHIVE" -C "$DEST"
        ;;
    application/zip)
        unzip -q "$ARCHIVE" -d "$DEST"
        ;;
    application/x-tar)
        tar -xf "$ARCHIVE" -C "$DEST"
        ;;
    application/x-7z-compressed)
        7z x "$ARCHIVE" -o"$DEST" -y > /dev/null
        ;;
    application/x-rar|application/vnd.rar)
        unrar x -o+ "$ARCHIVE" "$DEST" > /dev/null
        ;;
    *)
        echo "Error: Unsupported or unrecognized archive type: $MIME"
        exit 1
        ;;
esac

echo "Extracted '$ARCHIVE' to '$DEST'"

Breaking Down the Internals

Step 3: Adding a Fallback Based on File Extension

Sometimes file might not be installed, or the MIME type detection might be ambiguous. Let’s add an extension-based fallback:

detect_by_extension() {
    case "$ARCHIVE" in
        *.tar.gz|*.tgz) echo "application/gzip" ;;
        *.tar.bz2|*.tbz2) echo "application/x-bzip2" ;;
        *.tar.xz) echo "application/x-xz" ;;
        *.zip) echo "application/zip" ;;
        *.tar) echo "application/x-tar" ;;
        *.7z) echo "application/x-7z-compressed" ;;
        *.rar) echo "application/x-rar" ;;
        *) echo "unknown" ;;
    esac
}

if ! command -v file &>/dev/null; then
    MIME=$(detect_by_extension)
else
    MIME=$(file --mime-type -b "$ARCHIVE")
fi

This gives us a safety net: if the file command isn’t available, we fall back to guessing based on the filename’s suffix.

Step 4: Adding Progress and Verification

For large archives, silent extraction can feel like the script has hung. Let’s add basic progress feedback and a post-extraction verification step:

echo "Extracting, please wait..."
START=$(date +%s)

# ... extraction case statement runs here ...

END=$(date +%s)
DURATION=$((END - START))

echo "Done in ${DURATION}s."
echo "Verifying extracted contents..."

FILE_COUNT=$(find "$DEST" -type f | wc -l)
echo "Extracted $FILE_COUNT file(s) into '$DEST'."

date +%s returns the current Unix timestamp in seconds, and taking the difference between the start and end timestamps gives us a rough duration — a small touch, but it makes the tool feel more polished and informative.

Real-World Use Cases

Automating Decompression

Here’s a simple watch-folder automation that decompresses anything dropped into a downloads folder:

#!/usr/bin/env bash
WATCH_DIR="$HOME/Downloads"
EXTRACT_DIR="$HOME/Downloads/extracted"

inotifywait -m -e close_write --format "%f" "$WATCH_DIR" | while read -r filename; do
    ./decompress.sh "$WATCH_DIR/$filename" "$EXTRACT_DIR"
done

This uses inotifywait (from the inotify-tools package) to watch a directory and automatically run our decompression script whenever a new file finishes being written.

Security Considerations

Optimization Tips

Troubleshooting

Common Mistakes to Avoid

Frequently Asked Questions

What if I have a .tar.gz.gpg (encrypted) archive? You’ll need to decrypt it first with gpg -d archive.tar.gz.gpg > archive.tar.gz before running it through this tool.

Can this tool handle nested archives, like a zip inside a zip? Not automatically — you’d need to run the script twice, once on the outer archive and once on the extracted inner archive.

Does this work with password-protected archives? For .zip, unzip will prompt for a password interactively. For .7z and .rar, you can pass -p<password> to the respective commands, though hardcoding passwords in scripts is not recommended for security reasons.

Summary

We built a universal Bash decompression tool that detects archive types via MIME type (with an extension-based fallback), extracts using the correct native tool for each format, and adds basic safety and usability features like progress timing and file counting. The core lesson is that Bash’s case statement, combined with reliable type detection, is all you need to eliminate the mental overhead of remembering a dozen different extraction commands.

References

Exit mobile version