TAR. Архивируем грамотно

07/08/2018

Формат TAR

TAR, как формат, устроен макимально просто. Файлы в архиве располагаются один за другим, при этом метаданные каждого файла помещены перед его содержимым и, соответственно никак не зависят от других файлов, начала или конца архива. Это позволяет использовать tar для потоковой упаковки или распоковки. Потоковая упаковка и распаковка подразумевает собой отсутствие необходимости в промежуточной записи архива на носитель. Т.е. в случае, например с zip, вы должны сначала скачать архив, а потом его распаковать, а tar позволяет получить контент сразу. Подробнее об использовании таких пайплайнов в разделе Пайплайны.

Пока же начнем с простого действия - создадим tar архив

$ echo big_hippo > file_1
$ echo small_hippo > file_2
$ tar -c -f test.tar file_1 file_2

В примере выше 2 файла: file_1 и file_2 были созданы и запакованы в tar архив. Первый файл содержит текст "big_hippo", второй "small_hippo". Ключ "-c" или "--create" (допускается как длинная, так и короткая нотация) инструктирует tar создать архив, "-f" или "--file" задает имя для нового архива (без этого ключа tar выведет содержимое архива в консоль), а последовательность файлов в конце команды указывает, что именно нужно заархивировать.

В успешности создания архива можно убедиться с помощью утилиты file

$ file test.tar 
test.tar: POSIX tar archive (GNU)

Теперь, имея tar архив, мы можем его спокойно препарировать и посмотреть, что же у него реально внутри. Для этого мы будем использовать команду hexdump

$ hexdump -C test.tar

Самая левая колонка hexdump нам показывает смещение в байтах относительно начала файла, выраженное тем не менее в шестнадцатеричных числах. Т.е. число 200 - это не 200, а 512, если перевести его в десятичную систему исчисления. Для удобства восприятия мы выделили красным - метаданные, зеленым - данные, а синим - пустые данные. Т.е. tar к каждому файлу добавляет в начало 512 байт метаданных, содержащих имя файла, режим доступа, UID и GID владельца, контрольную сумму и др. поля. Иными словами с каждым новым файлом размер получаемого архива увеличивается как минимум на 512 байт. Минимум, потому что несмотря на то, что содержимое первого файла закончилось еще на отметке 20a (522-ой байт), tar намеренно добавляет в конец порцию нулей (выделено синим), чтобы итоговый размер был кратен 512.

Что же это получается, архиватор - не архиватор? На самом деле, tar лишь следует духу unix - одна программа решает одну проблему. Если вам нужна компрессия, обратитесь к другой программе. Это означает, что tar лишь склеивает файлы вместе, а о сжатии нужно заботиться отдельно. К счатью, разработчики смилостивились над бедными пользователями и добавили возможность автоматически (при создании архива) вызывать сторонний компрессор для проведения операции сжатия. Такими компрессорами являются gzip, xz, lzip, lzma, lzop и другие. Т.е. вы сначала должны подумать, какой компрессор вы хотите использовать, а потом попросить tar при помощи опций командной строки, после упаковки передать архив компрессору. Для каждого компрессора свой ключ. Самы популярный - это, конечно gzip.

Давайте воспользуемся нашим первым примером и сделает всё то же самое, только добавим сжатие:

$ tar -c -z -f test.tar.gz file_1 file_2
$ file test.tar.gz
test.tar.gz: gzip compressed data, last modified: Mon Jul  9 20:20:55 2018, from Unix, original size 10240

Ключ -z инструктирует tar воспользоваться gzip для совершения компресии. Обратили внимание, что расшиение (суффикс) файла изменилось на tar.gz? Это памятка пользователю, что архив был создан с использованием компрессии, и не просто какой-то, а gzip. Другой функции двойное расширение не несет - можно и без него

$ tar -c -J -f test.tar.xz file_1 file_2
$ file test.tar.xz
test.tar.xz: XZ compressed data

Ключ "-J" используется для проведения компрессии с использованием алгоритма xz. На выходе получаем соответственно архив с расширением tar.xz. Ключ "-j" сжимает bzip2, а вот ключам "--lzma", "--lzip" и "--lzop" повезло меньше - они лишены коротких аналогов. За то, понятно с первого взгляда, какой компрессор будет использован

Но на этом сюрпризы tar не заканчиваются. Внимательный человек во время просмотра дампа hexdump, непременно заострит внимание на строке, содержащей слово "ustar". Действительно, оказывается tar - это обобщенное название можества схожих форматов архивов со своими преимуществами и недостатками и, внимание, зачастую НЕСОВМЕСТИМЫХ между собой. Попытаться понять с каким форматом работает ваш tar можно выполнив команду

$ tar --help

И где-то в конце вероятно будет подсказка

*This* tar defaults to:
--format=gnu -f- -b20 --quoting-style=escape --rmt-command=/usr/lib/tar/rmt

Как видно, форматом по-умолчанию в данной реализации выбран gnu. Однако, как мы уже говорили ранее, такая подсказка может и не быть выведена. Все зависит от того из каких исходных текстов была собрана ваша реализация tar и с какими параметрами. Приведем список известных на сегодняшний день форматов tar:

  • gnu
  • oldgnu
  • v7
  • ustar
  • star
  • posix

Самым перспективным форматом tar явлется posix. Он снимает ограничения на размер файла в 8GB, также позволяет использовать неограниченный диапазон UID и GID пользователя. Уходят в прошлое и ограничение на длинну файла, а также добавляются несколько новых заголовков, которые позволяют сохранять больше информации об архивируемых файлах, например дата и время модификации файла. Так что если ваш парк машин выглядит достаточно современно, стоит задуматься о переводе формата posix в вашей инфраструктуре из разряда экспериментальных фич в стандарты.

Опции командной строки

Теперь давайте поговорим об опциях командной строки, которые помогают выжимать из tar максимум. Приведем несколько примеров

Распаковка tar в папку

TAR всегда распаковывает в текущую папку. Если вы хотите распаковать tar в другую папку, то у вас всего два пути: сделать в эту папку "cd" до начала распаковки, или попросить tar при помощи ключа "-C" сделать "cd" за вас. Для успешного проведения операции папка должна существовать

$ mkdir /srv/files
$ cd /srv/files
$ tar -xvf ~/test.tar

Или

$ mkdir /srv/files
$ tar -xvf test.tar -C /srv/files

Извлечь один файл

TAR позволяет извлекать одиночные файлы из архива, вы всего лишь должны указать в конце командной строки имя файла. Но тут есть одна странность, точнее то, что пользователи Windows не понимают. Имя файла - это не имя файла самого, а имя плюч весь путь от начала иерархии. Иными словами, если файл с именем "file_1" лежит прямо в корне иерархии, то проблем никаких, а вот если вообразить, что файл лежит в папке, скажем "test", то имя его "test/file_1". Это нужно, чтобы tar в момент распаковки смог правильно воссоздать структуру папок, ведь в архиве никаких папок нет, все файлы идут друг за другом - вспоминайте вывод hexdump. Приведем пример как, используя наш архив test.tar извлечь из него всего один файл file_1

$ tar -xvf test.tar file_1
file_1
$ file files_1
file_1: ASCII text

Теперь давайте создадим новый архив, но сначала положим наши два файла в папку

$ mkdir test
$ mv file_1 file_2 ./test/
$ tar -cvf test.tar test/
test/
test/file_1
test/file_2

И попробуем извлечь файл file_1

$ tar -xvf test.tar file_1
tar: file_1: Not found in archive
tar: Exiting with failure status due to previous errors

Мы получили явное предупреждение, что такого файла в архиве нет. Но так и должно быть, мы упаковали файл с путем и извлекать должны с путем

$ tar -xvf test.tar test/file_1
test/file_1

Предсказуемо tar создал папку test и распаковал наш файл в нее. Но что, если мы забыли правильный путь? Конечно, можно попросить tar показать нам список файлов

$ tar -tf test.tar 
test/
test/file_1
test/file_2

Ключ "-t" или "--list" при указании имени архива через ключ "-f" или "--file" заставляет tar вывести список файлов. А если этот список большой и не удобный? Как нам распаковать один файл просто зная его имя? Ключ "--no-anchored" приходит на помощь. Он выключает интерпретацию путей

$ tar --no-anchored -xvf test.tar file_1
test/file_1

Файлы также можно извлекать и по маске. Например мы хотим извлечь все файлы, имена которых начинаются с букв "file_". Ключ --wildcards позволяет включать в имена файлов звездочки

$ tar --wildcards --no-anchored -xvf test.tar file_*
test/file_1
test/file_2

Ну и в завершение истории о распаковке представляем вам ключ "--strip" который позволяет отбросить родительские папки. Он может пригодиться, когда вы не хотите, чтобы tar воссоздавал иерархию папок

$ tar --strip=1 --wildcards --no-anchored -xvf test.tar file_*

Добавить файлы в tar архив

Как вы наверняка поняли из заголовка, в tar архив файлы еще можно и добавлять. Создади новый файл и добавим его в существующий архив. В этом нам помодет ключ "-r"

$ tar -tf test.tar
file_1
file_2
$ echo another_small_hippo > file_3
$ tar -rvf test.tar file_3 
file_3
$ tar -tf test.tar
file_1
file_2
file_3

Пайплайны

Как мы уже упомянали в начале статьи, tar можно использовать для потоковой упаковки/распаковки. Классический пример - это tar через ssh. "А чем scp не угодил?" - спросите вы. А тем, что tar позволяет контент сжимать перед передачей, а scp нет. Скажем мы хотим передать нашу папку test на хост 192.168.1.200, используя ssh

$ tar -cz ./test | ssh 192.168.1.200 "tar -xzv -C ${DIR}"

В данной примере мы сначала просим tar запаковать и сжать папку с использованием gz, потом передать ее на вход ssh, который затем через зашифрованный канал транслирует данные на другой хост, передавая их там местному tar для распаковки в папку, указанную вместо ${DIR}. Идеально, не правда ли?

Ссылки

Базовый формат tar - http://www.gnu.org/software/tar/manual/html_node/Standard.html

Форматы tar - http://www.gnu.org/software/tar/manual/html_node/Formats.html

Статьи по теме: 

Темы:

Добавить комментарий