Эффективно удалить файл (ы) из большого .tgz

14

Предположим, у меня есть сжатый tar-архивом gzip tarArchive.tgz (+100 файлов, всего + 5 ГБ).

Какой самый быстрый способ удалить все записи, соответствующие заданному шаблону имени файла, например префикс * .jpg, а затем снова сохранить остатки в tar-шаре gzip: ed?

Замена старого архива или создание нового не важны, в зависимости от того, что быстрее.

Аксель Виллгерт
источник

Ответы:

14

С GNU tarвы можете сделать:

pigz -d < file.tgz |
  tar --delete --wildcards -f - '*/prefix*.jpg' |
  pigz > newfile.tgz

С bsdtar:

pigz -d < file.tgz |
  bsdtar -cf - --exclude='*/prefix*.jpg' @- |
  pigz > newfile.tgz

( pigzбудучи многопоточной версией gzip).

Вы можете перезаписать файл поверх себя, например:

{ pigz -d < file.tgz |
    tar --delete --wildcards -f - '*/prefix*.jpg' |
    pigz &&
    perl -e 'truncate STDOUT, tell STDOUT'
} 1<> file.tgz

Но это довольно рискованно, особенно если результат оказывается менее сжатым, чем исходный файл (в этом случае второй pigzможет закончиться перезаписью областей файла, которые первый еще не прочитал).

Стефан Шазелас
источник
спасибо за ответ, проголосовал. на следующей неделе запустим тест производительности, чтобы увидеть, какой из них лучше подходит для моего архива и системы, и приму это.
Аксель Виллгерт
8

Не пренебрегайте простым способом: он может быть достаточно быстрым для вашей цели. С помощью avfs для доступа к архиву в виде каталога:

cd ~/.avfs/path/to/original.tar.gz\#
pax -w -s '/^.*\.jpg$//' | gzip >/path/to/filtered.tar.gz        # POSIX
tar -czf /path/to/filtered.tar.gz -s '/^.*\.jpg$//' .            # BSD
tar -czf /path/to/filtered.tar.gz --transform '/^.*\.jpg$//' .   # GNU

С более примитивными инструментами, сначала извлеките файлы, исключая .jpgфайлы, затем создайте новый архив.

mkdir tmpdir && cd tmpdir
<original.tar.gz gzip -d | pax -r -pe -s '/^.*\.jpg$//'
pax -w . | gzip >filtered.tar.gz
cd .. && rm -rf tmpdir

Если ваша смола имеет --exclude:

mkdir tmpdir && cd tmpdir
tar -xzf original.tar.gz --exclude='*.jpg'
tar -czf filtered.tar.gz .
cd .. && rm -rf tmpdir

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

Поддержка архиваторов, выполняющих роль транзитного прохода (т.е. чтение архива и запись архива), как правило, ограничена. GNU tar может удалять элементы из архива с --deleteопцией операции--deleteСообщалось, что эта опция работает должным образом, когда tarдействует как фильтр из stdinв stdout.»), И это, вероятно, ваш лучший вариант.

Вы можете сделать мощные архивные фильтры в несколько строк Python. Его tarfileбиблиотека может читать и писать из потоков без возможности поиска, и вы можете использовать произвольный код в Python для фильтрации, переименования, изменения…

#!/usr/bin/python
import re, sys, tarfile
source = tarfile.open(fileobj=sys.stdin, mode='r|*')
dest = tarfile.open(fileobj=sys.stdout, mode='w|gz')
for member in source:
    if not (member.isreg() and re.match(r'.*\.jpg\Z', member.name)):
        sys.stderr.write(member.name + '\n')
        dest.addfile(member, source.extractfile(member))
dest.close()
Жиль "ТАК - перестань быть злым"
источник
Он также будет искажать uid / usernames, если он запускается от имени пользователя root, если это не выполняется на компьютере, на котором имя пользователя с таким же uid <=> сопоставлено с тем, на котором изначально был создан файл tar. ACL, расширенные атрибуты также могут быть затронуты. С tar, вы можете добавить pопцию.
Стефан
2

С tar, который поставляется на Mac OSX, вы можете сделать это:

tar -czf b.tgz --exclude '*.jpg' @a.tgz
mv b.tgz a.tgz
Джейк
источник
1

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

Это долго, и вам нужно достаточно свободного места на диске, но, насколько мне известно, нет другого способа сделать это.

Учитывая, что у вас уже есть такой путь /tmpdir/withalotofspace, достаточно свободного места (проверьте его, используя df -h /tmpdir/withalotofspace ), вы можете сделать что-то вроде этого:

$ cd /tmpdir/withalotofspace
$ tar -xvfz /path/to/compressedArchive.tgz
$ find /tmpdir/withalotofspace/ -type f -iname '*.jpg' -delete
$ tar -cvzf /path/to/purgedcompressedArchive.tgz .
DavAlPi
источник
Как показывают другие ответы, через трубопроводы нет необходимости хранить несжатые данные на диске в любой момент
Тобиас Кинцлер,
0

Мне нравится ответ @Gilles, за исключением того, что он может быть еще более упрощен. После распаковки, например, gunzip foo.tgzфайл будет foo.tarи файлы могут быть удалены сtar -f foo.tar --delete file|directory . Ниже приведен пример удаления каталога из файла tar.

    phablet@ubuntu-phablet:~/Downloads$ tar -cvf moo.tar moo1/
    moo1/
    moo1/moo2/
    moo1/moo2/moo3/
    moo1/moo2/moo3/moo4/
    moo1/moo2/moo3/moo4/moo5/
    phablet@ubuntu-phablet:~/Downloads$ tar -tf moo.tar 
    moo1/
    moo1/moo2/
    moo1/moo2/moo3/
    moo1/moo2/moo3/moo4/
    moo1/moo2/moo3/moo4/moo5/
    phablet@ubuntu-phablet:~/Downloads$ tar -f moo.tar --delete "moo1/moo2/moo3"
    phablet@ubuntu-phablet:~/Downloads$ tar -tf moo.tar 
    moo1/
    moo1/moo2/

Определенные типы файлов могут быть найдены с tar -tf foo.tar|egrep -i '.jpg$'.

Funmungus
источник