opscape

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...]

15 of 15 examples run in Debian 13.7 on 2026-10-31 · tar 1.35+dfsg-3.1 · manual: tar(1)

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
output
-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
output
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 logs
tar -tf backup.tar.gz
explain
output
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 logs
tar -tvf backup.tar.gz logs
explain
output
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 logs
mkdir restore
tar -xzf backup.tar.gz -C restore
ls restore restore/logs
output
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 logs
tar -xzvf backup.tar.gz data/hosts.txt
cat data/hosts.txt
output
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 logs
mkdir out
tar -xzvf backup.tar.gz -C out --wildcards '*.json'
output
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 logs
tar -xzOf backup.tar.gz data/poem.txt
explain
output
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.*
output
-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
output
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 src
mkdir app
tar -xzf app-2.4.tar.gz -C app --strip-components=1
ls app
output
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.txt
tar -rf notes.tar todo.txt
tar -tf notes.tar
output
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
output
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'
output
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 logs
tar -xzf backup.tar.gz data/missing.txt
explain
outputexit 2
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 dir creates an archive literally named z and tries to add out.tgz to it. Keep f last in the cluster, directly before the file name.

  • A tarball made with tar -czf a.tgz /home/me/project extracts to home/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 | head tells 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 warns file 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. Check tar --help on your system before relying on them.

  • bsdtar on macOS adds ._name AppleDouble files for extended attributes; set COPYFILE_DISABLE=1 when creating archives for Linux.

Options worth knowing

A selection, described in our own words. For the full list, see tar(1) or tar --help.

OptionWhat it does
-c, --createCreate a new archive.
-t, --listList the members of an archive.
-x, --extractExtract members from an archive.
-r, --appendAppend files to the end of an uncompressed archive.
-u, --updateAppend files only if they are newer than the copy already in the archive.
-d, --diffCompare archive members with the files on disk and report differences.
--deleteRemove members from an uncompressed archive.
-f, --file ARCHIVEUse ARCHIVE as the archive file; - means standard input or output.
-v, --verbosePrint each file name processed; with -t, an ls -l style listing.
-z, --gzipFilter the archive through gzip.
-j, --bzip2Filter the archive through bzip2.
-J, --xzFilter the archive through xz.
--zstdFilter the archive through zstd.
-a, --auto-compressPick the compressor from the archive name's suffix.
-I, --use-compress-program COMMANDFilter the archive through COMMAND, for example 'zstd -19'.
-C, --directory DIRChange to DIR before creating or extracting.
--exclude PATTERNSkip files whose names match the shell pattern PATTERN.
-X, --exclude-from FILERead exclude patterns from FILE, one per line.
--exclude-vcsSkip version-control directories such as .git.
--strip-components NRemove the first N path elements from member names on extraction.
--wildcardsTreat member name arguments as shell patterns.
-O, --to-stdoutExtract file contents to standard output instead of to files.
-k, --keep-old-filesDo not overwrite existing files when extracting; report them as errors.
--skip-old-filesSilently skip files that already exist when extracting.
-p, --preserve-permissionsRestore permissions exactly, ignoring the umask (the default for root).
--same-ownerRestore the archived owner (the default for root).
--numeric-ownerStore and restore owners by number instead of name.
-h, --dereferenceArchive the files that symlinks point to instead of the links.
-T, --files-from FILERead the names of files to archive from FILE.
-P, --absolute-namesKeep the leading / in member names.

Related