Linux zip Command
last modified October 23, 2026
The zip command creates ZIP archives, and unzip
lists, tests, and extracts them. Unlike tar, which bundles
files and leaves compression to a separate program, ZIP compresses each
file individually and keeps an index at the end of the archive. Single
files can therefore be listed, extracted, updated, or deleted without
unpacking the rest. ZIP is the most widely supported archive format on
Windows and macOS, which makes it a good choice for sharing files with
other systems.
The syntax is zip [options] archive.zip file.... The examples
use Info-ZIP Zip 3.0 and UnZip 6.0 on Debian, with a few text files, a
small website directory, a 100 KB JPEG file, and a 3 MB binary file.
Creating an archive
The first argument is the archive name, followed by the files to add.
zip archive.zip report.txt ls -l report.txt archive.zip
For each file, zip prints the method it used and how much
space it saved. The repetitive text file shrinks by 95%, from 10692 to 727
bytes. If the archive name has no extension, zip adds
.zip.
$ zip archive.zip report.txt adding: report.txt (deflated 95%) $ ls -l report.txt archive.zip -rw-r--r-- 1 jano jano 727 Oct 23 13:30 archive.zip -rw-r--r-- 1 jano jano 10692 Oct 20 10:00 report.txt
Adding files to an archive
Running zip on an existing archive adds new files and replaces
the ones already in it.
zip docs.zip notes.txt todo.txt zip docs.zip *.txt
The second command updates notes.txt and
todo.txt and adds report.txt. Very small files are
stored (kept uncompressed) because compression would not make them
smaller.
$ zip docs.zip notes.txt todo.txt adding: notes.txt (stored 0%) adding: todo.txt (stored 0%) $ zip docs.zip *.txt updating: notes.txt (stored 0%) updating: todo.txt (stored 0%) adding: report.txt (deflated 95%)
Archiving directories
To include the contents of a directory, the -r (recurse)
option is required.
zip nodir.zip website zip -r site.zip website
Without -r, only an empty directory entry is added, which is a
common mistake. With -r, all files and subdirectories are
included, with their paths relative to the current directory.
$ zip nodir.zip website adding: website/ (stored 0%) $ zip -r site.zip website adding: website/ (stored 0%) adding: website/css/ (stored 0%) adding: website/css/style.css (stored 0%) adding: website/cache/ (stored 0%) adding: website/cache/page1.tmp (stored 0%) adding: website/js/ (stored 0%) adding: website/js/app.js (stored 0%) adding: website/access.log (stored 0%) adding: website/index.html (deflated 16%)
Listing the contents
The -l option of unzip lists the files in an
archive without extracting them.
unzip -l site.zip zipinfo -1 site.zip
The listing shows the uncompressed size, the modification time, and the
name of each entry. zipinfo -1 prints only the names, which is
handy in scripts. unzip -v and plain zipinfo show
more details, such as the compressed size and the method.
$ unzip -l site.zip
Archive: site.zip
Length Date Time Name
--------- ---------- ----- ----
0 2026-10-21 14:30 website/
0 2026-10-21 14:30 website/css/
20 2026-10-21 14:30 website/css/style.css
0 2026-10-21 14:30 website/cache/
4 2026-10-21 14:30 website/cache/page1.tmp
0 2026-10-21 14:30 website/js/
19 2026-10-21 14:30 website/js/app.js
10 2026-10-21 14:30 website/access.log
32 2026-10-21 14:30 website/index.html
--------- -------
85 9 files
$ zipinfo -1 site.zip
website/
website/css/
website/css/style.css
website/cache/
website/cache/page1.tmp
website/js/
website/js/app.js
website/access.log
website/index.html
Excluding files
The -x option excludes files that match the given patterns.
zip -r site-clean.zip website -x '*.log' 'website/cache/*'
The patterns are quoted so that zip, not the shell, matches
them against the paths stored in the archive. In zip patterns,
* also matches slashes, so '*.log' excludes log
files at any depth. The -i option works the other way and
includes only matching files.
$ zip -r site-clean.zip website -x '*.log' 'website/cache/*' adding: website/ (stored 0%) adding: website/css/ (stored 0%) adding: website/css/style.css (stored 0%) adding: website/js/ (stored 0%) adding: website/js/app.js (stored 0%) adding: website/index.html (deflated 16%)
Updating and freshening
The -u option adds new files and replaces changed ones. The
-f option only replaces changed files that are already in the
archive.
echo "p { color: navy; }" >> website/css/style.css
zip -r -u site.zip website
echo "new page" > website/about.html
zip -r -f site.zip website
unzip -l site.zip | tail -n 1
zip -u compares modification times and rewrites only
style.css. The new file about.html is not added by
-f; only the directory entry, whose time changed when the file
was created, is freshened. The archive still has 9 entries.
$ echo "p { color: navy; }" >> website/css/style.css
$ zip -r -u site.zip website
updating: website/css/style.css (deflated 5%)
$ echo "new page" > website/about.html
$ zip -r -f site.zip website
freshening: website/ (stored 0%)
$ unzip -l site.zip | tail -n 1
104 9 files
Deleting files from an archive
The -d option removes entries from an archive.
zip -d site.zip 'website/cache/*' website/access.log unzip -l site.zip | tail -n 1
The archive is rewritten without the cache directory and the log file. The files on disk are not touched.
$ zip -d site.zip 'website/cache/*' website/access.log
deleting: website/cache/
deleting: website/cache/page1.tmp
deleting: website/access.log
$ unzip -l site.zip | tail -n 1
90 6 files
Compression levels
The options -0 to -9 set the compression level.
-0 stores files without compression, -9 compresses
best, and the default is -6.
zip -q -0 store.zip report.txt zip -q -9 best.zip report.txt ls -l store.zip best.zip zip media.zip photo.jpg
The -q option suppresses the progress messages. Higher levels
save little on most data and take more time. Formats that are already
compressed, such as JPEG, MP4, or other ZIP files, do not shrink at all, as
the last command shows; storing them with -0 is faster.
$ zip -q -0 store.zip report.txt $ zip -q -9 best.zip report.txt $ ls -l store.zip best.zip -rw-r--r-- 1 jano jano 731 Oct 23 13:30 best.zip -rw-r--r-- 1 jano jano 10862 Oct 23 13:30 store.zip $ zip media.zip photo.jpg adding: photo.jpg (deflated 0%)
Archiving without paths
The -j (junk paths) option stores only the file names, without
their directories.
zip -j flat.zip website/index.html website/css/style.css unzip -l flat.zip
The files end up in the top level of the archive. If two files have the same name in different directories, only one of them can be stored this way.
$ zip -j flat.zip website/index.html website/css/style.css
adding: index.html (deflated 16%)
adding: style.css (deflated 5%)
$ unzip -l flat.zip
Archive: flat.zip
Length Date Time Name
--------- ---------- ----- ----
32 2026-10-21 14:30 index.html
39 2026-10-23 13:30 style.css
--------- -------
71 2 files
Password protection
The -e option encrypts the files and asks for a password
twice.
zip -e secret.zip notes.txt unzip -l secret.zip unzip -P wrong secret.zip -d out1 unzip -P s3cret secret.zip -d out1
The password is not echoed. The file names stay visible, only the contents
are encrypted. Without -P, unzip asks for the
password; -P passes it on the command line, where other users
can see it in the process list, so use it only for tests.
zip uses the traditional PKWARE encryption (ZipCrypto), which
is weak and can be broken with known-plaintext attacks. For sensitive data,
use an AES-encrypted archive created with 7-Zip, or encrypt the archive with
gpg.
$ zip -e secret.zip notes.txt
Enter password:
Verify password:
adding: notes.txt (stored 0%)
$ unzip -l secret.zip
Archive: secret.zip
Length Date Time Name
--------- ---------- ----- ----
34 2026-10-20 10:00 notes.txt
--------- -------
34 1 file
$ unzip -P wrong secret.zip -d out1
Archive: secret.zip
skipping: notes.txt incorrect password
$ unzip -P s3cret secret.zip -d out1
Archive: secret.zip
extracting: out1/notes.txt
Splitting an archive
The -s option splits an archive into parts of a given size,
for example for upload limits.
zip -q -s 1m big.zip video.bin ls -l big.* unzip -q big.zip -d out2 cat big.z01 big.z02 big.zip > joined.zip unzip -q joined.zip -d out2 cmp video.bin out2/video.bin && echo identical
The parts are named .z01, .z02, and so on, and
the last one is .zip. UnZip 6.0 cannot read split archives
directly. Joined in order with cat, the parts can be
extracted; unzip prints warnings, but cmp confirms
that the file is intact. The documented way to rejoin the parts is
zip -s 0 big.zip --out whole.zip, but in our test, Zip 3.0
produced a corrupt archive when there were more than two parts. Always
verify the result.
$ zip -q -s 1m big.zip video.bin $ ls -l big.* -rw-r--r-- 1 jano jano 1048576 Oct 23 13:30 big.z01 -rw-r--r-- 1 jano jano 1048576 Oct 23 13:30 big.z02 -rw-r--r-- 1 jano jano 903480 Oct 23 13:30 big.zip $ unzip -q big.zip -d out2 warning [big.zip]: zipfile claims to be last disk of a multi-part archive; attempting to process anyway, assuming all parts have been concatenated together in order. Expect "errors" and warnings...true multi-part support doesn't exist yet (coming soon). file #1: bad zipfile offset (local header sig): 4 $ cat big.z01 big.z02 big.zip > joined.zip $ unzip -q joined.zip -d out2 warning [joined.zip]: zipfile claims to be last disk of a multi-part archive; attempting to process anyway, assuming all parts have been concatenated together in order. Expect "errors" and warnings...true multi-part support doesn't exist yet (coming soon). warning [joined.zip]: 2097152 extra bytes at beginning or within zipfile (attempting to process anyway) file #1: bad zipfile offset (local header sig): 2097156 (attempting to re-compensate) $ cmp video.bin out2/video.bin && echo identical identical
Reading file names from standard input
The -@ option reads the list of files to add from standard
input.
find website -name '*.html' -o -name '*.css' | zip -@ web-src.zip
This lets find select the files by any criteria: name, size,
age, or owner. The new about.html is included.
$ find website -name '*.html' -o -name '*.css' | zip -@ web-src.zip adding: website/about.html (stored 0%) adding: website/css/style.css (deflated 5%) adding: website/index.html (deflated 16%)
Extracting archives
unzip extracts into the current directory, or into the
directory given with -d.
unzip site.zip -d restore unzip -n site.zip -d restore unzip -o -q site.zip -d restore unzip -q site.zip website/css/style.css -d only-css find only-css -type f unzip -p site.zip website/css/style.css
If a file already exists, unzip asks whether to replace it.
-n never overwrites existing files, and -o
overwrites them without asking. File names after the archive name extract
only those entries. The -p option writes the contents to
standard output, so a file can be read without creating it on disk.
$ unzip site.zip -d restore
Archive: site.zip
creating: restore/website/
creating: restore/website/css/
inflating: restore/website/css/style.css
creating: restore/website/js/
extracting: restore/website/js/app.js
inflating: restore/website/index.html
$ unzip -n site.zip -d restore
Archive: site.zip
$ unzip -o -q site.zip -d restore
$ unzip -q site.zip website/css/style.css -d only-css
$ find only-css -type f
only-css/website/css/style.css
$ unzip -p site.zip website/css/style.css
body { margin: 0; }
p { color: navy; }
Testing an archive
The -t option of unzip checks the integrity of
every entry without extracting anything.
unzip -t site.zip head -c 400 site.zip > truncated.zip unzip -t truncated.zip
For a good archive, each entry is reported as OK. The
truncated copy is missing the central directory at the end of the file, so
unzip cannot read it at all. Test archives after transferring
them and before deleting the originals.
$ unzip -t site.zip
Archive: site.zip
testing: website/ OK
testing: website/css/ OK
testing: website/css/style.css OK
testing: website/js/ OK
testing: website/js/app.js OK
testing: website/index.html OK
No errors detected in compressed data of site.zip.
$ head -c 400 site.zip > truncated.zip
$ unzip -t truncated.zip
Archive: truncated.zip
End-of-central-directory signature not found. Either this file is not
a zipfile, or it constitutes one disk of a multi-part archive. In the
latter case the central directory and zipfile comment will be found on
the last disk(s) of this archive.
unzip: cannot find zipfile directory in one of truncated.zip or
truncated.zip.zip, and cannot find truncated.zip.ZIP, period.
Best practices for zip
- Use -r for directories: Without it, only an empty directory entry is stored.
- Quote patterns: Quote
-xand-ipatterns so that the shell does not expand them. - List before extracting: Use
unzip -lto see whether an archive has a top-level directory, and extract with-dinto a new one. - Don't rely on ZipCrypto: Use 7-Zip with AES or
gpgfor confidential data. - Test archives: Run
unzip -tafter creating or transferring an archive. - Use tar on Linux: For backups between Linux systems, tar preserves ownership, permissions, and symbolic links more completely.
Source
In this article, we have used the zip and unzip
commands to create, list, update, encrypt, split, extract, and test ZIP
archives.
Author
List all Linux tutorials.