SkillsLinux

Archives

Packing and unpacking with tar, gzip and zip, the formats you will meet in releases and backups.

Topic 18 of 1810 min read

Software releases, backups and log history usually arrive as a single file that holds many others. On Linux that file is most often a tar archive, compressed with gzip or xz. You also meet zip files, mostly from other systems. Knowing how to pack, check and unpack them safely is part of almost every install and every backup.

By the end you can create, list and extract tar archives with and without compression, compress single files, work with zip files, unpack into the directory you choose, and check that a download is the file it claims to be.

tar: create, list, extract

tar joins many files and directories into one file, keeping names, permissions, owners and timestamps. On its own it does not compress anything. The name comes from tape archive, which is where backups used to go.

A tar command starts with letters that say what to do:

LetterMeaning
cCreate an archive
tList what is in an archive
xExtract an archive
vVerbose: print each file name as it goes
fThe next argument is the archive file name

You always need exactly one of c, t or x, and almost always f. Because f takes the next argument, keep it last in the group: -cvf site.tar, not -cfv site.tar.

The examples use a small website directory with one large log. Create it in your home directory:

$ mkdir -p site/css site/logs
$ echo "<h1>Hello</h1>" > site/index.html
$ echo "body { margin: 0; }" > site/css/main.css
$ seq 1 100000 | sed 's|^|GET /api/orders 200 |' > site/logs/access.log
$ du -sh site
2.5M	site

Now create an archive of the site directory:

$ tar -cvf site.tar site
site/
site/logs/
site/logs/access.log
site/css/
site/css/main.css
site/index.html

The order of the names depends on the filesystem, so yours may differ. List what is inside without extracting it. Add v to see sizes, owners and dates:

$ tar -tvf site.tar
drwxr-xr-x root/root         0 2026-10-24 11:19 site/
drwxr-xr-x root/root         0 2026-10-24 11:19 site/logs/
-rw-r--r-- root/root   2588895 2026-10-24 11:19 site/logs/access.log
drwxr-xr-x root/root         0 2026-10-24 11:19 site/css/
-rw-r--r-- root/root        20 2026-10-24 11:19 site/css/main.css
-rw-r--r-- root/root        15 2026-10-24 11:19 site/index.html

Extract it with x. Files are written relative to the directory you are in, so this recreates site/ here, replacing any files with the same names:

$ tar -xf site.tar

To extract a single file, name it after the archive, exactly as the listing shows it: tar -xf site.tar site/index.html.

If you archive an absolute path, tar strips the leading / so that extracting never writes over system files by accident:

$ tar -cf hostname.tar /etc/hostname
tar: Removing leading `/' from member names
$ tar -tf hostname.tar
etc/hostname

Compression

A plain tar file is as big as the files in it. Add one more letter to compress it as it is written:

LetterCompressorUsual extensionNotes
zgzip.tar.gz or .tgzFast, everywhere, the default choice
Jxz.tar.xzSmaller files, slower to create
jbzip2.tar.bz2Older; you still see it in downloads

The Docker Ubuntu image does not include xz. Install it first:

$ apt update && apt install -y xz-utils

Then compare the sizes:

$ tar -czf site.tar.gz site
$ tar -cJf site.tar.xz site
$ ls -lh site.tar*
-rw-r--r-- 1 root root 2.5M Oct 24 11:19 site.tar
-rw-r--r-- 1 root root 259K Oct 24 11:19 site.tar.gz
-rw-r--r-- 1 root root  33K Oct 24 11:21 site.tar.xz

This log shrinks a lot because it repeats itself; real logs usually compress to between a fifth and a tenth of their size. Files that are already compressed, such as images or zip files, barely shrink at all.

When listing or extracting, modern tar detects the compression on its own, so tar -tf site.tar.xz and tar -xf site.tar.gz both work. You only need the letter when creating.

gzip and xz also work on single files, without tar. They replace the file with a compressed copy:

$ cp site/logs/access.log app.log
$ gzip app.log
$ ls app.log*
app.log.gz
$ gunzip app.log.gz
$ ls app.log*
app.log

gzip -k keeps the original. zcat prints a compressed file without unpacking it, so you can pipe it straight into grep or head. This is how you read old logs in /var/log, which are rotated into files like syslog.2.gz:

$ gzip -k app.log
$ zcat app.log.gz | head -n 2
GET /api/orders 200 1
GET /api/orders 200 2

xz and unxz work the same way, and xzcat is their zcat.

Choosing where files go

-C changes to a directory before tar does anything else. When extracting, it puts the files somewhere other than where you are standing. The directory must already exist:

$ mkdir restore
$ tar -xzf site.tar.gz -C restore
$ ls restore
site

Put -C before any file names. tar reads its arguments in order, and a -C placed after a file name does not apply to that file.

When creating, -C decides how the paths inside the archive start. Archiving . from inside site gives an archive with no site/ directory at the top:

$ tar -czf flat.tar.gz -C site .
$ tar -tf flat.tar.gz
./
./logs/
./logs/access.log
./css/
./css/main.css
./index.html

--exclude leaves out files that match a pattern. Put it before the directory you archive:

$ tar -czf site-nologs.tar.gz --exclude="site/logs" site
$ tar -tf site-nologs.tar.gz
site/
site/css/
site/css/main.css
site/index.html

A pattern can also use wildcards: --exclude="*.log" skips every file ending in .log wherever it is, but keeps the empty logs/ directory. You can give --exclude several times.

Check before you extract

Extracting writes files with whatever names the archive holds, and quietly replaces files that already exist. Before you extract something you did not make, list it first:

$ tar -tf flat.tar.gz | head

Look for two things:

  • Does everything sit under one top-level directory? A well-made release unpacks into tool-1.0/. An archive like flat.tar.gz puts its files straight into the current directory and scatters them among yours. Extract that kind into a new, empty directory with -C.
  • Are any paths absolute, or do any contain ..? Those could write outside the directory you chose. GNU tar refuses most of them, but you should still notice.

v while extracting prints each file as it is written, so you can see afterwards what changed.

zip and unzip

zip files combine archiving and compression in one format. They are common for files shared with Windows and macOS users, and for some downloads. Ubuntu does not always include the tools:

$ apt install -y zip unzip

zip -r archives a directory. The archive name comes first, then what to put in it:

$ zip -r site.zip site
  adding: site/ (stored 0%)
  adding: site/logs/ (stored 0%)
  adding: site/logs/access.log (deflated 90%)
  adding: site/css/ (stored 0%)
  adding: site/css/main.css (stored 0%)
  adding: site/index.html (stored 0%)

unzip -l lists, and unzip -d extracts into a directory, creating it if it does not exist:

$ unzip -l site.zip
Archive:  site.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  2026-10-24 11:22   site/
        0  2026-10-24 11:22   site/logs/
  2588895  2026-10-24 11:22   site/logs/access.log
        0  2026-10-24 11:22   site/css/
       20  2026-10-24 11:22   site/css/main.css
       15  2026-10-24 11:22   site/index.html
---------                     -------
  2588930                     6 files
$ unzip -q site.zip -d /tmp/unzipped

-q stops unzip printing every file. To leave files out, use -x with a pattern: zip -r site.zip site -x "site/logs/*". zip does not keep Linux owners, so prefer tar for backups of a Linux system.

Checking a download

A checksum is a short fingerprint of a file's contents. Change one byte and the checksum changes completely. Projects publish a SHA-256 checksum next to each download so you can confirm the file arrived intact and is the one they released.

sha256sum prints a file's checksum:

$ sha256sum tool-1.0.tar.gz
4252d05864...  tool-1.0.tar.gz

The checksum is 64 characters long; it is shortened here, and you make this file in Task 2. Comparing long strings by eye is unreliable. Instead, put the published checksum in a file in the same format, and let sha256sum -c compare:

$ sha256sum -c tool-1.0.tar.gz.sha256
tool-1.0.tar.gz: OK

If the file has changed, you see FAILED and a non-zero exit code. Do not extract a file that fails the check; download it again, and if it still fails, stop. When a project publishes only the bare checksum, build the line yourself, with two spaces between the checksum and the name:

$ echo "<checksum from the website>  tool-1.0.tar.gz" | sha256sum -c

Tasks

Do these in your Linux shell, in your home directory.

Task 1: Back up a project

Create this project:

$ mkdir -p ~/project/src ~/project/logs ~/project/cache
$ echo 'print("hello")' > ~/project/src/main.py
$ echo "# Project" > ~/project/README.md
$ echo "started" > ~/project/logs/app.log
$ echo "tmp" > ~/project/cache/data.bin

Make a gzip-compressed backup called project-backup.tar.gz that leaves out logs and cache. List it to check, restore it into /tmp/restore, and compare the restored copy with the original using diff -r.

Check your work
$ cd ~
$ tar -czf project-backup.tar.gz --exclude="project/logs" --exclude="project/cache" project
$ tar -tzf project-backup.tar.gz
project/
project/README.md
project/src/
project/src/main.py
$ mkdir -p /tmp/restore
$ tar -xzf project-backup.tar.gz -C /tmp/restore
$ diff -r project /tmp/restore/project
Only in project: cache
Only in project: logs

diff -r reports only the two directories you excluded, so everything else came back identical. If tar says /tmp/restore: Cannot open, you skipped the mkdir. If the listing still shows logs, check that the exclude pattern starts with project/, the same way the paths in the listing do.

Task 2: Verify and install a download

First make a "download" and its published checksum, the way a project would:

$ mkdir -p /tmp/publisher/build/bin
$ printf '#!/bin/sh\necho "tool 1.0"\n' > /tmp/publisher/build/bin/tool
$ chmod +x /tmp/publisher/build/bin/tool
$ echo "tool 1.0" > /tmp/publisher/build/README
$ tar -czf /tmp/publisher/tool-1.0.tar.gz -C /tmp/publisher/build .
$ cd /tmp/publisher && sha256sum tool-1.0.tar.gz > tool-1.0.tar.gz.sha256
$ mkdir ~/downloads && cp /tmp/publisher/tool-1.0.tar.gz* ~/downloads/

Now, working in ~/downloads, check the archive against its checksum, look inside it, and install it so that /opt/tool-1.0/bin/tool runs. Then append a line to the archive with echo x >> tool-1.0.tar.gz and check it again.

Check your work
$ cd ~/downloads
$ sha256sum -c tool-1.0.tar.gz.sha256
tool-1.0.tar.gz: OK
$ tar -tzf tool-1.0.tar.gz
./
./bin/
./bin/tool
./README
$ mkdir -p /opt/tool-1.0
$ tar -xzf tool-1.0.tar.gz -C /opt/tool-1.0
$ /opt/tool-1.0/bin/tool
tool 1.0
$ echo x >> tool-1.0.tar.gz
$ sha256sum -c tool-1.0.tar.gz.sha256
tool-1.0.tar.gz: FAILED
sha256sum: WARNING: 1 computed checksum did NOT match

The listing shows no top-level directory, so the files must go into a directory you create. If you extracted without -C, bin/ and README landed in ~/downloads. Delete them and extract again.

Quiz

1. What do the letters in tar -xzvf backup.tar.gz mean, and which one is unnecessary on a modern system?

Show answer

x extracts, z uses gzip, v lists each file as it is written, and f names the archive. z is unnecessary when extracting, because GNU tar detects the compression itself.

2. You are on a server and a colleague sends you fix.tar.gz to unpack in /srv/app, which is full of live files. What do you do before extracting?

Show answer

List it with tar -tzf fix.tar.gz and read the paths. Check whether they sit under one directory, whether any would replace files you care about, and whether any are absolute or contain ... If you are unsure, extract into an empty directory with -C, look at the result, and copy across only what you need.

3. You want to read yesterday's rotated log, syslog.1.gz, without leaving an unpacked copy behind. How?

Show answer

Use zcat /var/log/syslog.1.gz, piped into grep, less or tail as needed. It prints the uncompressed text and leaves the file as it is. gunzip would replace the .gz file with the unpacked one.

4. A download page shows a SHA-256 checksum, and sha256sum on your file prints a different one. What does that mean and what should you do?

Show answer

The file is not the one that was published. It may be incomplete, damaged, or replaced. Do not extract or run it. Download it again from the official source, and if the checksum still does not match, stop and find out why.