tar: основы архивирования
tar — это самая старая (с 1979 года) и до сих пор самая используемая утилита Linux/UNIX для архивирования. Аббревиатура расшифровывается как Tape ARchive — изначально для записи на ленточные накопители. Сейчас ленту никто не использует, но tar остался как стандарт упаковки множества файлов в один.
В DE-сценариях tar встречается ежедневно: бэкапы директорий, distribution package (.tar.gz файлы — это tar + gzip), packaging Python пакетов (.tar.gz есть на PyPI), Docker image layers (tar внутри).
В этом уроке — базовые операции с tar.
Что делает tar
Tar объединяет много файлов в один. Не сжимает — для сжатия используются отдельные утилиты (gzip, bzip2, xz, zstd). Это историческая особенность: tar родился до compression utilities, и был придуман для записи на ленту (которая сама может или не может сжимать).
Поэтому стандартный паттерн:
- tar.gz (или .tgz) = tar + gzip — самый распространённый
- tar.bz2 (или .tbz2) = tar + bzip2 — смалпее но медленнее
- tar.xz (или .txz) = tar + xz — ещё меньше но ещё медленнее
- tar.zst = tar + zstd — современный, быстрый и хорошее сжатие
Архивирование != сжатие. tar делает archive, сжатие — отдельный шаг
Главные modes: c, x, t
У tar три основных режима работы:
Без указания режима tar не знает, что делать
Запомнить мнемонику: Create, eXtract, lisT. В команде эти буквы — первый аргумент:
# Создать архив
tar c ...
# Распаковать
tar x ...
# Просмотреть
tar t ...
Флаги-модификаторы: v, f
Без модификаторов tar — молчаливый и неудобный. Главные:
v(verbose) — выводить имена файлов по мере обработки.f(file) — следующий аргумент это имя архива. БЕЗftar пишет/читает stdin/stdout!
# Без f — tar пытается читать с tape device /dev/st0 (или похожего)
# Этот флаг практически всегда нужен
# Create + verbose + file: создать архив archive.tar с содержимым dir/
tar cvf archive.tar dir/
# Extract + verbose + file
tar xvf archive.tar
# List + verbose + file
tar tvf archive.tar
Современный стиль допускает дефис: tar -cvf (как обычные команды). Но многие пишут без дефиса по привычке — оба варианта работают.
Часто видишь команду tar xvf archive.tar без понимания, что значат буквы. Запомните: f ВСЕГДА должно быть, иначе tar попробует общаться с tape device. x или c — выбор режима. v — опционально.
Сжатие inline: z, j, J, —zstd
Чтобы не делать tar | gzip отдельным шагом, tar умеет интегрировать сжатие:
Один шаг — tar + compression
# Создать tar.gz
tar czvf archive.tar.gz dir/
# Распаковать tar.gz
tar xzvf archive.tar.gz
# Аналогично для bzip2, xz
tar cjvf archive.tar.bz2 dir/
tar xjvf archive.tar.bz2
tar cJvf archive.tar.xz dir/
tar xJvf archive.tar.xz
# Для zstd — отдельный флаг
tar --zstd -cvf archive.tar.zst dir/
tar --zstd -xvf archive.tar.zst
Современный tar (gnu-tar) умеет автоопределение по расширению при extract:
# tar сам поймёт что это gzip по расширению
tar xvf archive.tar.gz
tar xvf archive.tar.bz2
tar xvf archive.tar.xz
tar xvf archive.tar.zst
Поэтому при extract достаточно xvf, не указывая флаг сжатия. При create — нужно указать.
Создание: примеры
# Базовое — содержимое директории
tar czvf backup.tar.gz /home/levo/data/
# Несколько объектов в один архив
tar czvf logs.tar.gz /var/log/airflow/ /var/log/postgres/
# Из текущей директории
cd /home/levo/data
tar czvf ~/backup.tar.gz .
# . = текущая директория. В архиве будут пути relative от текущей
# С абсолютными путями (плохо — при распаковке создаст /home/levo/...)
tar czvf bad.tar.gz /home/levo/data/
# Лучше — относительные пути
cd /home/levo && tar czvf data-backup.tar.gz data/
# Архив stdin (без файла, прямо в pipe)
tar czf - dir/ | ssh remote "cat > /backup/dir.tar.gz"
# - = stdout. Удобно для пайпов.
# С exclude
tar czvf code.tar.gz --exclude='*.pyc' --exclude='__pycache__' /home/levo/project/
Извлечение
# Базовое
tar xvf archive.tar.gz
# В конкретную директорию
tar xvf archive.tar.gz -C /tmp/extract-here/
# Только конкретный файл
tar xvf archive.tar.gz path/to/file.txt
# Удалить топовую директорию (часто нужно)
tar xvf archive.tar.gz --strip-components=1
# Например, архив содержит project-v1.0/src/main.py
# Без --strip — извлечётся в project-v1.0/src/main.py
# С --strip-components=1 — извлечётся в src/main.py (без project-v1.0)
--strip-components=N — must-know для распаковки source-tarball-ов. GitHub release tarball часто содержит repo-v1.0/ как верхнюю папку — tar xvf release.tar.gz --strip-components=1 распаковывает прямо в текущую директорию без обёртки.
Просмотр содержимого
# Список файлов в архиве
tar tvf archive.tar.gz
# Только имена (без размера/прав/времени)
tar tf archive.tar.gz
# С деталями: размер, права, время
tar tvf archive.tar.gz
# Сколько файлов в архиве
tar tf archive.tar.gz | wc -l
# Поиск по содержимому
tar tf archive.tar.gz | grep "specific-pattern"
tv показывает что-то типа:
drwxr-xr-x levo/levo 0 2026-05-13 14:23 data/
-rw-r--r-- levo/levo 1024 2026-05-13 14:23 data/file1.csv
-rw-r--r-- levo/levo 2048 2026-05-13 14:23 data/file2.csv
Колонки: permissions, owner/group, size, modification time, path.
Полезные опции
# Дозапись в существующий архив (только non-compressed!)
tar rvf archive.tar new-file.txt
# Сравнение архива с файлами (что изменилось?)
tar dvf archive.tar dir/
# Сохранять более точные timestamps
tar --acls --xattrs -czvf archive.tar.gz dir/
# --acls = POSIX ACLs
# --xattrs = extended attributes
# Не сохранять absolute paths
tar -P # сохранять (по умолчанию выключено)
tar -p # сохранять permissions (по умолчанию для root)
# Архивировать список файлов
tar czvf docs.tar.gz -T files.txt
# -T = take filenames from this file (one per line)
Пример с -T:
# Найти все CSV в /var/data
find /var/data -name '*.csv' > files-to-backup.txt
# Архивировать
tar czvf csvs.tar.gz -T files-to-backup.txt
Полезно когда список файлов больше, чем шrинка command-line.
DE-сценарии
1. Бэкап перед миграцией
# Перед изменением конфига Airflow — бэкап
sudo tar czvf /backup/airflow-config-$(date +%F).tar.gz /etc/airflow/
# Или с timestamp поминутным
sudo tar czvf /backup/airflow-config-$(date +%FT%H%M%S).tar.gz /etc/airflow/
2. Передача папки на другой сервер
# Архивировать локально
tar czf project.tar.gz ./project/
# Отправить через rsync (он умеет сжатие на лету, но для удобства можно tar)
rsync -avhP project.tar.gz remote:/tmp/
# На той стороне
ssh remote "cd /tmp && tar xzf project.tar.gz"
Альтернативно — pipe через ssh:
# Архивировать + передать в pipe без temporary file
tar czf - project/ | ssh remote "cd /destination && tar xzf -"
- означает stdout (на create) или stdin (на extract). Удобно когда нет места для temporary archive file.
3. Архивировать только старые файлы
# Найти .log файлы старше 7 дней, заархивировать
find /var/log/airflow -name '*.log' -mtime +7 -print0 | \
tar czvf logs-archive-$(date +%F).tar.gz --null -T -
# Затем удалить (после проверки!)
find /var/log/airflow -name '*.log' -mtime +7 -delete
--null + -T - — читать имена файлов из stdin, разделённых nul-байтами. Безопаснее для файлов с пробелами/спецсимволами.
4. Распаковать Python source tarball
# Скачали с PyPI
curl -O https://files.pythonhosted.org/.../mypackage-1.0.tar.gz
# Извлечь без обёртки
mkdir mypackage && cd mypackage
tar xvf ../mypackage-1.0.tar.gz --strip-components=1
# Установить
pip install .
5. Inspect Docker image
# Docker image — это tar архив layers
docker save myimage:latest > myimage.tar
# Посмотреть структуру
tar tvf myimage.tar | head
# Это даст:
# manifest.json
# repositories
# <layer-hash>/layer.tar
# ...
Подводные камни
1. Absolute paths в архиве
# Плохо — архив содержит /home/levo/data/...
tar czvf bad.tar.gz /home/levo/data/
# При распаковке: tar xvf bad.tar.gz создаст /home/levo/... — не то место
Хорошо — относительные пути:
cd /home/levo && tar czvf data.tar.gz data/
# или
tar czvf data.tar.gz -C /home/levo data/
# -C сменить директорию перед добавлением
Современный tar предупреждает: «Removing leading `/’ from member names» — это безопасный default.
2. Permissions при распаковке от root vs user
Если архив создан от root (с разными owner) и распаковывается обычным пользователем — owner станет ваш user (вы не можете chown). Используйте —no-same-owner если хотите оставить как есть, или распакуйте от root.
3. Файлы с одинаковыми именами
# Если в архиве есть data/file.txt, и в текущей директории тоже —
# tar перепишет существующий
tar xvf archive.tar.gz
# Не переписывать существующие
tar xvf archive.tar.gz --skip-old-files
# Переписать только если файл в архиве новее
tar xvf archive.tar.gz --keep-newer-files
Попробуй сам
# 1. Создать тестовую директорию
mkdir -p /tmp/tar-test/dir1/sub
echo "file1" > /tmp/tar-test/dir1/file1.txt
echo "file2" > /tmp/tar-test/dir1/sub/file2.txt
echo "file3" > /tmp/tar-test/dir1/file3.txt
# 2. Создать tar (без сжатия)
cd /tmp/tar-test
tar cvf archive.tar dir1/
ls -lh archive.tar
# 3. Создать tar.gz (со сжатием)
tar czvf archive.tar.gz dir1/
ls -lh archive.tar*
# 4. Посмотреть содержимое
tar tvf archive.tar.gz
# 5. Распаковать в другую директорию
mkdir extracted
tar xvf archive.tar.gz -C extracted/
ls -R extracted/
# 6. С --strip-components
mkdir extracted2
tar xvf archive.tar.gz --strip-components=1 -C extracted2/
ls -R extracted2/
# 7. Извлечь конкретный файл
tar xvf archive.tar.gz dir1/file1.txt
cat dir1/file1.txt
# 8. Через pipe
tar czf - dir1/ | (cd /tmp && tar xzf -)
# 9. С исключениями
tar czvf no-sub.tar.gz dir1/ --exclude='*/sub/*'
tar tvf no-sub.tar.gz
# 10. Cleanup
rm -rf /tmp/tar-test
Cross-link: следующий урок 02 — детально про форматы сжатия (gzip vs zstd). Урок 04 — production DE workflows с tar.