web-dev-qa-db-ja.com

大きな.tgzからファイルを効率的に削除する

Gzip圧縮されたtar-ball圧縮されたArchive.tgz(+100ファイル、合計+ 5GB)があると仮定します。

たとえば、prefix * .jpgのような特定のファイル名パターンに一致するすべてのエントリを削除し、残りをgzip:ed tar-ballに再度保存する最も速い方法は何ですか?

古いアーカイブを置き換えるか、新しいアーカイブを作成するかは、どちらが最速でも重要ではありません。

14
Aksel Willgert

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

pigzgzipのマルチスレッドバージョンです)。

次のようにファイルを上書きすることができます:

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

しかし、特に結果が元のファイルよりも圧縮されない場合(特に、2番目のpigzが、最初のファイルがまだ読み取っていないファイルの領域を上書きしてしまう可能性がある場合)は、非常に危険です。

14

簡単な方法を軽視しないでください:それはあなたの目的のために十分速いかもしれません。 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

Tarに--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オプションは、tarstdinから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()

Mac OSXに付属のtarを使用すると、次のことができます。

tar -czf b.tgz --exclude '*.jpg' @a.tgz
mv b.tgz a.tgz
2
Jake

これを行うには、おそらくローカルディレクトリにある.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 .
1
DavAlPi

@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$'で見つけることができます。

0
Funmungus

私が使う:

tar -xvf myLarge.gz --exclude "prefix" | tar -czvf myLarge.gz -T -

この意志:

  1. "prefix"を含むファイルを除くすべてのファイルを抽出します
  2. -T -)パイプレストをtarにパイプし、myLarge.gzを再圧縮します。
0
Cyborg