web-dev-qa-db-ja.com

パイプ内に別のtarファイルを作成して、tarファイルの内容をフィルタリングするにはどうすればよいですか?

アクセス許可、mtimesなど、保持したいさまざまな属性を持ついくつかのディレクトリを含む外部システムからの単一のtarファイルを考えてみます。これらのファイルのサブセットを(rootではなく)通常のユーザーとして簡単に取得するにはどうすればよいですか?

次のようなものを探します:

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

このtarアーカイブの主な属性(所有権、グループ、モード、mtime)を保持することも重要です。 拡張ヘッダーキーワード など、tarファイルの他の属性についてはどうですか?

このサブディレクトリに巨大なファイルが含まれている場合に一時ディレクトリの使用を回避するソリューションのボーナスポイント。

13
Lekensteyn

bsdtar(libarchiveに基づく) は、tar(およびその他のアーカイブ)をstdinからstdoutにフィルターできます。たとえば、 一致するファイル名のみを通過 パターンを使用して、s/old/new/の名前変更を行うことができます。 Ubuntuのbsdtarなど、ほとんどのディストリビューション用にすでにパッケージ化されています。

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には-を使用できます。


私の検索でも、javascriptを使用して必要なアーカイブの変更を定義するように見えるこのストリーミングtar変更ツールが見つかりました。 (私はすべてがjsで書かれていると思います)。

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

14
Peter Cordes

最も簡単な方法は、アーカイブ全体をコピーすることです。大きすぎてやりたくないと思います。

通常のコマンドラインツール(tarpax)は、アーカイブのメンバーを別のアーカイブにコピーすることをサポートしていません。

所有権を保持する必要がない場合は、 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として実行する必要があり、ファイルがローカルマシンとリモートホスト間で異なる名前またはIDを持つアカウントによって所有されている場合、意図した結果が得られない可能性があります。

Pythonの tarfile ライブラリは、tarメンバーを操作するかなり簡単な方法を提供するため、あるtarファイルから別のtarファイルにそれらをシャッフルできます。 POSIX標準形式(ustar、pax)といくつかのGNU拡張機能をサポートします。テストされていないPythonスクリプトがtarファイルを読み取る(おそらくgzipで圧縮されている)またはbzip2)を標準入力に入力し、bzip2で圧縮されたtarファイルを標準出力に書き込みます。スクリプトに渡された引数で始まる場合、ソースのメンバーがコピーされます。

#!/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

GNU tarには--deleteオプションがあります:

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

このように、出力に含めるnotを指定することにより、入力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 ...
0
Karel Vlk

特権のない別のアプローチは、 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
0
Lekensteyn