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

  • MIME=$(file --mime-type -b "$ARCHIVE") — the -b flag (“brief”) strips the filename from the output so we get just the MIME type string, which is easier to parse in a case statement.
  • The case "$MIME" in ... esac block is Bash’s pattern-matching construct. It’s more readable than a long chain of if/elif statements when you have many possible values to check against.
  • tar -xzf breaks down as: -x (extract), -z (decompress with gzip), -f (read from the specified file). Similarly -j handles bzip2 and -J handles xz — tar has built-in support for multiple compression algorithms via these flags, so we don’t need separate gzip/bzip2/xz calls.
  • unzip -q "$ARCHIVE" -d "$DEST" — the -q flag suppresses the file-by-file listing for cleaner output, and -d specifies the destination directory.
  • 7z x "$ARCHIVE" -o"$DEST" -y — note there’s no space between -o and the destination path; that’s required syntax for 7-Zip. The -y flag auto-confirms any prompts.
  • unrar x -o+ "$ARCHIVE" "$DEST" — -o+ tells unrar to overwrite existing files without asking.
  • The final *) case in our case statement acts as a catch-all “else” branch, printing an error for unsupported formats instead of failing silently.

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

  • Downloading software releases from GitHub, which come in varied formats depending on the project — .tar.gz for Linux binaries, .zip for cross-platform releases.
  • Server provisioning scripts that need to unpack configuration bundles or dependency archives without knowing in advance what format they’ll be in.
  • Digital forensics or migration work, where old backup archives might be in obscure or legacy formats.
  • CI/CD pipelines that pull build artifacts packaged in different formats from different upstream systems.

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

  • Beware of “zip bombs.” A tiny archive can expand into terabytes of data and exhaust your disk. Before extracting untrusted archives, check their uncompressed size with unzip -l or tar -tzvf first.
  • Watch for path traversal (“zip slip”) vulnerabilities. Malicious archives can contain entries like ../../etc/passwd designed to write outside the intended extraction directory. Modern versions of tar and unzip guard against this, but always keep your extraction tools updated, and consider extracting untrusted archives inside a sandboxed or disposable container.
  • Never extract untrusted archives as root. Run extraction as a low-privilege user so that even a malicious archive can’t overwrite system files.
  • Validate archive integrity when possible using checksums (sha256sum) before extracting anything downloaded from the internet.

Optimization Tips

  • For very large .tar.gz files, consider using pigz (parallel gzip) instead of standard gzip for faster decompression on multi-core machines: tar --use-compress-program=pigz -xf archive.tar.gz.
  • If you only need specific files from a large archive, extract selectively instead of the whole thing: tar -xzf archive.tar.gz path/to/specific/file.
  • Extracting to a fast disk (SSD) rather than a network share dramatically speeds up large extractions.

Troubleshooting

  • “gzip: stdin: not in gzip format” — the file extension doesn’t match its actual content; rely on the MIME-detection method shown above rather than trusting the extension blindly.
  • “unrar: command not found” — unrar isn’t included in most default repos due to licensing; install it manually via apt install unrar (may require enabling the multiverse or non-free repository).
  • Extraction succeeds but files are missing — check if the archive contains a single top-level folder; some archive tools nest content one level deeper than expected.
  • “Permission denied” when extracting — check that the destination directory is writable by your user, or that you’re not trying to extract into a system-protected path.

Common Mistakes to Avoid

  • Trusting file extensions blindly instead of verifying actual file type.
  • Extracting archives from untrusted sources without checking their contents first (tar -tzvf or unzip -l let you preview without extracting).
  • Not creating the destination directory before extraction, causing errors on some tools.
  • Forgetting that 7z and unrar syntax differs significantly from tar/unzip, leading to copy-paste mistakes.

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

Total
1
Shares

Leave a Reply

Previous Post
How to Create a Bash File Compression Tool

How to Create a Bash File Compression Tool

Next Post
How to Create a Bash File Recovery Tool

How to Create a Bash File Recovery Tool

Related Posts