Как я могу отфильтровать содержимое файла tar, создав другой файл tar в конвейере?

13

Рассмотрим один tar-файл из внешней системы, который содержит несколько каталогов с различными атрибутами, которые я хочу сохранить, такими как разрешения, mtimes и т. Д. Как я могу легко взять подмножество этих файлов как обычный пользователь (не root)?

Ищу что-то вроде:

tar -f some.tar.gz --subset subdir/ | ssh remote@system tar xvz

Также важно, чтобы основные атрибуты (владелец, группа, режим, mtime) в этом архиве tar были сохранены. Как насчет других атрибутов в файле tar, таких как расширенные ключевые слова заголовка ?

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

Lekensteyn
источник

Ответы:

14

bsdtar (на основе libarchive) может фильтровать tar (и некоторые другие архивы) из stdin в stdout. Например, он может передавать только имена файлов, соответствующие шаблону, и может s/old/new/переименовывать. Он уже упакован для большинства дистрибутивов, например, как bsdtarв Ubuntu.

sudo apt-get install bsdtar   # or aptitude, if you have it.

# example from the man page:
bsdtar -c -f new.tar --include='*foo*' @old.tgz
#create new.tar containing only entries from old.tgz containing the string ‘foo’
bsdtar -czf - --include='*foo*' @-  # filter stdin to stdout, with gzip compression of output.

Обратите внимание, что у вас есть широкий выбор форматов сжатия для ввода / вывода, поэтому вам не нужно вручную передавать через gunzip / lz4. Вы можете использовать -для stdin с @tarfileсинтаксисом и / или -для stdout как обычно.


Мой поиск также нашел этот инструмент изменения потокового tar, который, кажется, хочет, чтобы вы определяли изменения архива, которые вы хотите, используя javascript. (Я думаю, что все это написано в JS).

https://github.com/mafintosh/tar-stream

Питер Кордес
источник
1
Отлично, не знал, что такой @original.tarподход возможен с bsdtar. Кажется, работает с расширенными атрибутами и сжатием, а также </var/cache/pacman/pkg/libuv-1.7.0-1-x86_64.pkg.tar.xz bsdtar -czf - --include='usr/share/*' @- | tar tvz(и по какой-то причине пустой выбор производит серию нулевых байтов, но для меня это не является большой проблемой).
Лекенштейн
1
Согласно моим тестам, s/old/new/ он не работает с файлами из старых архивов, использующих @ old.tgz, он работает только с реальными файлами, архивируя их непосредственно из файловой системы. Это действительно позор, поскольку это был бы самый полезный вариант использования для меня.
Барт
4

Самый простой способ - скопировать весь архив; Я полагаю, вы не хотите этого делать, потому что он слишком большой.

Обычные инструменты командной строки ( tar, pax) не поддерживают копирование членов архива в другой архив.

Если вам не нужно сохранять права собственности, я бы предложил использовать файловые системы FUSE . Вы можете использовать archivemount для монтирования архива как файловой системы; сделайте это для исходного архива и запустите tar на смонтированной файловой системе.

archivemount some.tar.gz mnt
cd mnt
tar -cz subdir | ssh example.com tar -xz
fusermount -u mnt

В качестве альтернативы вы можете использовать AVFS :

mountavfs
cd ~/.avfs$PWD/some.tar.gz\#
tar -cz subdir | ssh example.com tar -xz

Кроме того, вы можете запустить tarисходный архив и распаковать его на удаленный компьютер через SSHFS .

sshfs example.com: mnt
cd mnt
tar -xf /path/to/some.tar.gz subdir
fusermount -u mnt

Однако все эти методы громоздки, если вам нужно сохранить право собственности. Все они включают извлечение в файл на локальном компьютере, поэтому владельцем этого файла должно быть предполагаемое удаленное владение. Это требует запуска от имени пользователя root и может не дать ожидаемого результата, если файлы принадлежат учетным записям, чьи имена или идентификаторы отличаются на локальном компьютере и на удаленном хосте.

tarfileБиблиотека Python предоставляет довольно простой способ манипулирования членами tar, поэтому вы можете перемещать их из одного файла tar в другой. Он поддерживает стандартные форматы POSIX (ustar, pax), а также некоторые расширения GNU. Вот непроверенный скрипт Python, который считывает файл tar (возможно, сжатый с помощью gzip или bzip2) на свой стандартный ввод и записывает файл tar, сжатый с помощью bzip2, на свой стандартный вывод. Члены из источника копируются, если они начинаются с аргумента, переданного сценарию.

#!/usr/bin/env python2
import sys, tarfile
source = tarfile.open(fileobj=sys.stdin)
destination = tarfile.open(fileobj=sys.stdout, mode='w:bz2')
for info in source:
    if info.name.startswith(sys.argv[1]):
        destination.addfile(info)
destination.close()

Быть призванным как

tar_filter <some.tar.gz subdir/ | ssh example.com tar -xj
Жиль "ТАК - прекрати быть злым"
источник
1
bsdtar (основанный на libarchive) может фильтровать архивы tar на лету, смотрите мой ответ.
Питер Кордес
Задача состояла в том, чтобы извлечь данные из образа прошивки, поэтому владение / членство в группе действительно важно. Подход Python может работать, хотя.
Лекенштейн
0

Альтернативный подход без привилегий - использовать fakerootпрограмму, чтобы притвориться, что вам разрешено менять владельца. В то время как другие атрибуты tar потеряны, он сохраняет режимы mtime и uid / gid. Эти команды создают временный каталог, извлекают подмножество файлов и, наконец, создают новый архив:

mkdir tmp
<some.tar.gz \
fakeroot -- sh -c 'cd tmp && tar -xzf- subdir/ && tar -czf- subdir' |
   ssh remote@system tar -xzvf-
rm -rf tmp
Lekensteyn
источник
0

У GNU tarесть --deleteопция:

$ tar -c a b c | tar --delete a | tar -t
b
c

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

К сожалению, я не смог получить --excludeопцию для работы --delete, так что сначала вам нужно получить явный список ( -t) вещей, которые нужно удалить, а затем передать его другому вызову tar.

$ tar --delete --no-recursion `tar -t --exclude subdir <some.tar` <some.tar | ssh ...

Или вы можете сохранить список во внешнем файле, если он слишком длинный или сложный:

$ tar -t --exclude subdir <some.tar >to_delete.lst
$ tar --delete --no-recursion -T to_delete.lst <some.tar | ssh ...
Карел Влк
источник
-1

Из того, что я знаю, tarкоманда не может использовать формат tar как для ввода, так и для вывода. Вам придется каким-то образом извлекать файлы локально и снова использовать tar для создания tar-файла на лету, что-то вроде этого ( -означает, что вместо файла используется стандартный ввод / вывод):

tar cf - subdir/ | ssh remote@system 'cd extractdir && tar xvf -'

Обратите внимание, tarчто возможность извлекать файл tarfile непосредственно из другого файла tarfile - интересная идея ...

Уриэль
источник
Без root это приведет к потере всей информации о владельце / группе, которую я явно хочу сохранить.
Лекенштейн
1
Вы должны отредактировать свой вопрос, указав, что у вас нет root-доступа на вашем хосте.
Уриэль