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-bflag (“brief”) strips the filename from the output so we get just the MIME type string, which is easier to parse in acasestatement.- The
case "$MIME" in ... esacblock is Bash’s pattern-matching construct. It’s more readable than a long chain ofif/elifstatements when you have many possible values to check against. tar -xzfbreaks down as:-x(extract),-z(decompress with gzip),-f(read from the specified file). Similarly-jhandles bzip2 and-Jhandles xz —tarhas built-in support for multiple compression algorithms via these flags, so we don’t need separategzip/bzip2/xzcalls.unzip -q "$ARCHIVE" -d "$DEST"— the-qflag suppresses the file-by-file listing for cleaner output, and-dspecifies the destination directory.7z x "$ARCHIVE" -o"$DEST" -y— note there’s no space between-oand the destination path; that’s required syntax for 7-Zip. The-yflag auto-confirms any prompts.unrar x -o+ "$ARCHIVE" "$DEST"—-o+tellsunrarto overwrite existing files without asking.- The final
*)case in ourcasestatement 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.gzfor Linux binaries,.zipfor 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 -lortar -tzvffirst. - Watch for path traversal (“zip slip”) vulnerabilities. Malicious archives can contain entries like
../../etc/passwddesigned to write outside the intended extraction directory. Modern versions oftarandunzipguard 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.gzfiles, consider usingpigz(parallel gzip) instead of standardgzipfor 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” —
unrarisn’t included in most default repos due to licensing; install it manually viaapt install unrar(may require enabling themultiverseornon-freerepository). - 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 -tzvforunzip -llet you preview without extracting). - Not creating the destination directory before extraction, causing errors on some tools.
- Forgetting that
7zandunrarsyntax differs significantly fromtar/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.