Ефективно видаліть файли з великих .tgz


14

Припустимо, у мене є gzip-стислий тар-кулька compressionArchive.tgz (+100 файлів, загальною кількістю + 5 Гб).

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

Заміна старого архіву або створення нового не важлива, залежно від того, що відбувається найшвидше.


Відповіді:


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 / user, якщо вони виконуються як root, якщо це не робиться на машині, яка має те саме відображення uid <=> імені користувача, що і те, де спочатку створювався файл tar. Також можуть впливати ACL, розширені атрибути. З tar, ви можете додати pопцію.
Стефан Шазелас

2

З дьогтем, який надходить на 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 .

Як показують інші відповіді, через трубопроводи немає необхідності зберігати нестиснені дані на диску в будь-який момент
Тобіас Кіенцлер

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$'.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.