Archives and compression
tar
Bundle files and directories into one archive, list it, or extract it; optionally compressed with gzip, bzip2, xz or zstd.
tar {-c|-t|-x|-r|-u} [-f ARCHIVE] [OPTION...] [FILE...]
tar packs a tree of files into a single stream (a "tarball") that keeps names, directories,
permissions, owners and timestamps, and unpacks it again. On its own it does not compress;
the compressors are separate programs that tar runs for you: -z for gzip, -j for
bzip2, -J for xz and --zstd for zstd. A .tar.gz is just a tar stream that went
through gzip.
Every invocation needs exactly one mode letter: -c create, -t list, -x extract, -r
append, -u update. Then -f NAME says which archive file to work on; without it GNU tar
falls back to its built-in default device (usually standard input or output), which is rarely what you want.
Because -f takes the next word as its argument, keep it last in a cluster: -czf out.tgz,
never -cfz.
Paths inside the archive are stored as you typed them, relative to the current directory.
That decides where things land on extraction, so archive from the parent directory (or use
-C) to get clean names like src/main.c. When extracting, GNU tar detects the compression
by itself, so tar -xf file.tar.xz works without -J. For a single file you only want to
compress, use the compressor directly; for a Windows-friendly archive, zip.
Examples
Create an archive of a directory
tar -cf src.tar src
ls -l src.tar
-rw-r--r-- 1 dev dev 10240 Oct 31 20:21 src.tar
-c creates, -f src.tar names the output. Nothing is printed on success. The archive is
uncompressed: tar pads every member to 512-byte blocks and the whole file to a 10 KiB
record, which is why a few small files make a 10240-byte tarball.
Create a gzip-compressed tarball
tar -czvf backup.tar.gz data logs
ls -l backup.tar.gz
data/ data/numbers.txt data/windows.txt data/inventory.tsv data/hosts.txt data/fruits.txt data/config.json data/settings.conf data/sales.csv data/users.csv data/poem.txt data/servers.json logs/ logs/app.log logs/error.log logs/app.log.1 -rw-r--r-- 1 dev dev 1909 Oct 31 20:21 backup.tar.gz
-z pipes the stream through gzip and -v prints each path as it is added: each
directory, then its contents in the order the filesystem returns them (not sorted). The result is a fraction of the uncompressed size because text
compresses well.
List what is inside an archive
Setup run before this example
tar -czf backup.tar.gz data logstar -tf backup.tar.gz
data/ data/hosts.txt data/fruits.txt data/settings.conf data/servers.json data/config.json data/poem.txt data/numbers.txt data/windows.txt data/inventory.tsv data/users.csv data/sales.csv logs/ logs/error.log logs/app.log logs/app.log.1
-t lists member names without extracting anything. Directories end in /. GNU tar
noticed the gzip compression on its own, so -z was not needed.
List with permissions, owners, sizes and dates
Setup run before this example
tar -czf backup.tar.gz data logstar -tvf backup.tar.gz logs
drwxrwxr-x dev/dev 0 2026-10-25 17:10 logs/ -rw-rw-r-- dev/dev 112 2026-10-27 00:38 logs/error.log -rw-rw-r-- dev/dev 150 2026-10-27 13:12 logs/app.log.1 -rw-rw-r-- dev/dev 729 2026-10-27 03:10 logs/app.log
With -v, the listing looks like ls -l: mode, owner/group, size, modification time and
name. Naming logs after the archive restricts the listing to that directory.
Extract into another directory with -C
Setup run before this example
tar -czf backup.tar.gz data logsmkdir restore
tar -xzf backup.tar.gz -C restore
ls restore restore/logs
restore: data logs restore/logs: app.log app.log.1 error.log
-x extracts and -C restore changes into restore first, so the archived paths
data/... and logs/... are recreated under it. Without -C, they land in the current
directory and silently overwrite files with the same names.
Extract a single file
Setup run before this example
tar -czf backup.tar.gz data logstar -xzvf backup.tar.gz data/hosts.txt
cat data/hosts.txt
data/hosts.txt web01 10.0.0.11 web02 10.0.0.12 db01 10.0.0.21 cache01 10.0.0.31
Name the member exactly as tar -t shows it. Only that file is written (into
data/, which already existed here); everything else in the archive is skipped.
Extract members that match a pattern
Setup run before this example
tar -czf backup.tar.gz data logsmkdir out
tar -xzvf backup.tar.gz -C out --wildcards '*.json'
data/config.json data/servers.json
--wildcards makes the member arguments shell-style patterns, matched against the stored
names. Quote the pattern so your shell does not expand it against local files first.
Print a file from an archive without extracting it
Setup run before this example
tar -czf backup.tar.gz data logstar -xzOf backup.tar.gz data/poem.txt
The quick brown fox jumps over the lazy dog. The dog sleeps; the fox runs on.
-O (--to-stdout) sends the member's contents to standard output instead of writing a
file, handy for a quick look or for piping into another command.
Compare gzip, bzip2, xz and zstd
tar -czf logs.tar.gz logs
tar -cjf logs.tar.bz2 logs
tar -cJf logs.tar.xz logs
tar --zstd -cf logs.tar.zst logs
ls -lS logs.tar.*
-rw-r--r-- 1 dev dev 684 Oct 31 20:21 logs.tar.xz -rw-r--r-- 1 dev dev 682 Oct 31 20:21 logs.tar.bz2 -rw-r--r-- 1 dev dev 655 Oct 31 20:21 logs.tar.gz -rw-r--r-- 1 dev dev 643 Oct 31 20:21 logs.tar.zst
The same three log files under each compressor, sorted by size with ls -S. On inputs
this small each format's fixed overhead dominates and the sizes end up within a few dozen
bytes; on large text, xz usually compresses best and slowest, zstd and gzip fastest. With
-a instead of a letter, tar picks the compressor from the suffix (tar -caf logs.tar.xz logs).
Leave files out with --exclude
tar -czf src.tar.gz --exclude='*.py' --exclude=Makefile src
tar -tzf src.tar.gz
src/ src/util.sh src/main.c src/lib/
Each --exclude pattern is matched against the names below the directory being archived,
so both Python files and the Makefile are skipped. Put --exclude before the paths to
archive: written after them, GNU tar 1.35 warns that it has no effect and exits with
status 2.
Drop leading directories when extracting
Setup run before this example
tar -czf app-2.4.tar.gz srcmkdir app
tar -xzf app-2.4.tar.gz -C app --strip-components=1
ls app
Makefile app.py lib main.c util.sh
The archive's names all start with src/. --strip-components=1 removes that first path
element, so the contents land directly in app/. This is the usual way to unpack a
release tarball whose top directory has a version number you do not want.
Add a file to an existing archive
Setup run before this example
tar -cf notes.tar notes.txttar -rf notes.tar todo.txt
tar -tf notes.tar
notes.txt todo.txt
-r appends members to the end of an uncompressed archive. On a compressed one tar stops
with Cannot update compressed archives: decompress, append, and compress again.
Why tar strips the leading slash
tar -cf todo.tar /home/dev/demo/todo.txt
tar -tf todo.tar
tar: Removing leading `/' from member names home/dev/demo/todo.txt
Absolute paths are stored relative (home/dev/...), and tar says so on standard error. That
protects you on extraction: the file is recreated under the current directory instead of
overwriting /home/dev/demo/todo.txt. Use -C /home/dev/demo todo.txt to store just
todo.txt.
Copy a directory tree to a remote host through a pipe
tar -czf - src | ssh server 'mkdir -p copy && tar -xzf - -C copy'
ssh server 'find copy -type f'
copy/src/main.c copy/src/util.sh copy/src/app.py copy/src/Makefile copy/src/lib/helpers.py
-f - makes tar write the archive to standard output; the remote tar reads it from
standard input. Nothing is stored in between, and permissions and timestamps survive. (The
"remote" in these examples is the sandbox itself, reached over ssh.)
Asking for a file that is not in the archive
Setup run before this example
tar -czf backup.tar.gz data logstar -xzf backup.tar.gz data/missing.txt
tar: data/missing.txt: Not found in archive tar: Exiting with failure status due to previous errors
tar reports each name it could not find and exits with status 2, so a script can tell a partial extraction from a full one.
Common pitfalls
Option order with
-f:tar -cfz out.tgz dircreates an archive literally namedzand tries to addout.tgzto it. Keepflast in the cluster, directly before the file name.A tarball made with
tar -czf a.tgz /home/me/projectextracts tohome/me/project/.... Archive relative paths, or use-C /home/me project, to get the layout you expect.Extracting overwrites existing files without asking. List first (
tar -tf), or extract into an empty directory with-C, especially for archives from someone else;--keep-old-files(-k) refuses to overwrite.Some archives have no top-level directory and spray files into the current directory (a "tarbomb").
tar -tf archive | headtells you before you extract.tar -czf backup.tgz .run from inside the directory being archived can try to include the archive itself; tar notices and warnsfile changed as we read it. Write the archive somewhere else.
GNU vs BSD and macOS
The examples above ran the GNU/Linux version. These are the differences that matter on macOS and the BSDs (written from their documentation, not run here).
macOS ships bsdtar (libarchive). The common options (
-c -t -x -z -j -J -f -C -v,--exclude,--strip-components) work the same; bsdtar also reads zip and other formats.GNU-only spellings include
--wildcards,--zstd(newer bsdtar has it too) and--exclude-vcs. Checktar --helpon your system before relying on them.bsdtar on macOS adds
._nameAppleDouble files for extended attributes; setCOPYFILE_DISABLE=1when creating archives for Linux.
Options worth knowing
A selection, described in our own words. For the full list, see tar(1) or tar --help.
| Option | What it does |
|---|---|
-c, --create | Create a new archive. |
-t, --list | List the members of an archive. |
-x, --extract | Extract members from an archive. |
-r, --append | Append files to the end of an uncompressed archive. |
-u, --update | Append files only if they are newer than the copy already in the archive. |
-d, --diff | Compare archive members with the files on disk and report differences. |
--delete | Remove members from an uncompressed archive. |
-f, --file ARCHIVE | Use ARCHIVE as the archive file; - means standard input or output. |
-v, --verbose | Print each file name processed; with -t, an ls -l style listing. |
-z, --gzip | Filter the archive through gzip. |
-j, --bzip2 | Filter the archive through bzip2. |
-J, --xz | Filter the archive through xz. |
--zstd | Filter the archive through zstd. |
-a, --auto-compress | Pick the compressor from the archive name's suffix. |
-I, --use-compress-program COMMAND | Filter the archive through COMMAND, for example 'zstd -19'. |
-C, --directory DIR | Change to DIR before creating or extracting. |
--exclude PATTERN | Skip files whose names match the shell pattern PATTERN. |
-X, --exclude-from FILE | Read exclude patterns from FILE, one per line. |
--exclude-vcs | Skip version-control directories such as .git. |
--strip-components N | Remove the first N path elements from member names on extraction. |
--wildcards | Treat member name arguments as shell patterns. |
-O, --to-stdout | Extract file contents to standard output instead of to files. |
-k, --keep-old-files | Do not overwrite existing files when extracting; report them as errors. |
--skip-old-files | Silently skip files that already exist when extracting. |
-p, --preserve-permissions | Restore permissions exactly, ignoring the umask (the default for root). |
--same-owner | Restore the archived owner (the default for root). |
--numeric-owner | Store and restore owners by number instead of name. |
-h, --dereference | Archive the files that symlinks point to instead of the links. |
-T, --files-from FILE | Read the names of files to archive from FILE. |
-P, --absolute-names | Keep the leading / in member names. |
Related
gzipCompress files to .gz (replacing the original by default), or decompress them with -d or gunzip.bzip2Compress files to .bz2 with the Burrows-Wheeler algorithm, or decompress them with -d, bunzip2 or bzcat.xzCompress files to .xz with LZMA2 for high ratios, or decompress them with -d, unxz or xzcat.zstdCompress files to .zst with Zstandard, fast at any ratio; decompress with -d, unzstd or zstdcat.zipPackage and compress files into a .zip archive, the format every operating system opens natively.unzipList, test or extract the files in a .zip archive.rsyncCopy and synchronize files and directories, locally or over ssh, transferring only what changed.scpCopy files between hosts over ssh, with cp-like syntax where a remote path is written HOST:PATH.