Download and decompress ZIP, tar.gz, and gzip files with cURL
Pipe cURL into tar or gzip to decompress a download as it arrives. For ZIP files, download to a
temporary file first, then run unzip. The examples below check both the download and extraction,
and refuse to reuse an existing output directory.
Basic cURL commands for downloading
Use Bash on Linux, cURL, GNU tar, GNU gzip, Info-ZIP UnZip, and the standard mkdir, mktemp,
and rm utilities. The commands were tested on Ubuntu 24.04 with Bash 5.2, cURL 8.5.0, GNU tar 1.35,
gzip 1.12, and UnZip 6.00. They are not PowerShell or generic sh commands.
Choose the block for your format and replace its https://example.com/… URL with a trusted download
URL. Run the complete block, including the parentheses, in Bash from a directory you own and can
write to. Each block stands alone. You can also save one as extract.sh and run bash extract.sh.
The cURL options used here are:
-f: Return an error for HTTP failures such as 404, without passing the error page to the extractor.-sS: Hide the progress meter while still printing errors.-L: Follow redirects.-o: Write the download to the specified file instead of standard output.
The output directory must not exist, even if it is empty. mkdir reserves it before any download
starts; do not change that command to mkdir -p. The cleanup trap is installed only after creation
succeeds, so a repeated command leaves the previous result untouched. umask 077 makes newly
created working directories private to your user, though archive entries can carry their own modes.
Handling different archive formats
Zip archives (.zip)
Info-ZIP UnZip 6.0 does not read an archive from stdin. Its -p option sends extracted files to
stdout; it does not accept a ZIP pipe as input. The UnZip manual
documents this limitation. Use a regular downloaded file so the extractor can seek through it.
(
umask 077
dest='./zip-output'
mkdir -- "$dest" || exit
trap 'rm -rf -- "$dest"' EXIT
work=$(mktemp -d) || exit
trap 'rm -rf -- "$work" "$dest"' EXIT
curl -fsSL 'https://example.com/archive.zip' -o "$work/archive.zip" || exit
unzip -q "$work/archive.zip" -d "$dest" </dev/null || exit
rm -rf -- "$work" || exit
trap - EXIT
printf 'Extracted to %s\n' "$dest"
)
On success, the files are under ./zip-output/, with their archived directory structure intact.
mktemp -d creates a unique temporary directory in $TMPDIR, or /tmp when unset. Only that
private copy of archive.zip is removed. A file named temp.zip in your current directory is never
used. This example is for unencrypted ZIP files; closing stdin prevents an interactive input prompt
from holding up the command.
tar gzip archives (.tar.gz or .tgz)
A gzip-compressed tar archive can stream directly into GNU tar:
(
set -o pipefail
umask 077
dest='./tar-output'
mkdir -- "$dest" || exit
trap 'rm -rf -- "$dest"' EXIT
curl -fsSL 'https://example.com/archive.tar.gz' | tar -xzf - -C "$dest" || exit
trap - EXIT
printf 'Extracted to %s\n' "$dest"
)
Here, -x extracts, -z decompresses gzip, -f - reads the archive from stdin, and -C chooses the
extraction directory. Files land under ./tar-output/; no compressed archive is saved. If the
archive contains a top-level directory, that directory remains inside tar-output.
Bash’s pipefail option makes the
pipeline fail when either cURL or tar fails. Without it, a successful extractor can hide a failed
transfer, even one that reports an error after delivering usable archive bytes. The || exit
returns the failure status from the subshell and triggers cleanup. The parentheses keep these shell
settings and exit calls from affecting your interactive session.
Gzip archives (.gz)
Plain gzip compresses a byte stream; it does not provide a directory tree. Choose the output filename
explicitly with gzip -dc and a redirection:
(
set -o pipefail
umask 077
dest='./gzip-output'
mkdir -- "$dest" || exit
trap 'rm -rf -- "$dest"' EXIT
curl -fsSL 'https://example.com/file.gz' | gzip -dc > "$dest/payload" || exit
trap - EXIT
printf 'Decompressed to %s/payload\n' "$dest"
)
-d decompresses and -c writes to stdout. The result is ./gzip-output/payload, including when the
original file is empty or contains binary data. The enclosing directory prevents the redirection
from truncating an older result. If a file named .gz actually contains a tar archive, use the tar
example with that URL to extract its members.
Security considerations
Use these commands for archives from a source you trust, as your normal user. The fresh directory
protects existing output from accidental reuse; it is not a sandbox. Archive paths, links,
permissions, extractor vulnerabilities, and excessive expanded size still matter. GNU tar’s
security guidance explains the
risks of extracting untrusted archives. --strip-components changes path layout; it does not make
an arbitrary archive safe.
Streaming starts writing files before the transfer has finished. Wait for a zero exit status and the success message before using them. On a download or extraction error, the trap removes the newly created output directory. A forced termination or a cleanup permission error can leave partial files behind; inspect that directory before choosing a new destination for a retry.
Common pitfalls and troubleshooting tips
- The destination already exists: Choose a new
destname. The command deliberately fails before downloading, preserving the existing directory, file, or symbolic link. - cURL reports an HTTP or connection error: Check the download URL and access requirements. A web page returning HTTP 200 is still not an archive; the extractor must accept the downloaded content too.
- The extractor reports unexpected EOF, a checksum error, or invalid format: Treat the result
as failed. Confirm the format and obtain a complete copy from the publisher. A
.gzsuffix alone does not tell you whether its contents are a tar archive. - A transfer keeps breaking: Rerun the entire block after resolving the cause. Do not simply add retry flags to a pipe: cURL cannot retract bytes already sent downstream. Its retry documentation warns that redirected output can receive duplicate data.
- Extraction runs out of space: Allow room for the expanded files. The ZIP example also needs space for the downloaded archive in the temporary directory.
Choose when to save the archive
Streaming tar.gz and gzip saves the disk space of the compressed download. If you need to compare a publisher’s checksum or verify a signature before extracting, download to a fresh working directory first and verify that file. An extractor accepting the archive establishes that it could read it; it does not establish who published it.
