如何过滤 tar 文件的内容,在管道中生成另一个 tar 文件?

如何过滤 tar 文件的内容,在管道中生成另一个 tar 文件?

考虑来自外部系统的单个 tar 文件,其中包含一些具有我想要保留的各种属性的目录,例如权限、mtimes 等。我如何轻松地将这些文件的子集作为普通用户(而不是 root)获取?

寻找类似的东西:

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

保留此 tar 存档中的主要属性(所有权、组、模式、mtime)也很重要。 tar 文件中的其他属性(例如扩展标题关键字

如果该子目录包含巨大文件,则避免使用临时目录的解决方案的奖励点。

答案1

bsdtar(基于libarchive)可以从标准输入到标准输出过滤 tar(和其他一些档案)。例如,它可以仅传递匹配的文件名一个模式,并且可以s/old/new/重命名。它已经针对大多数发行版进行了打包,例如Ubuntu。bsdtar libarchive-tools

sudo apt-get install libarchive-tools   # 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

答案2

最简单的方法是复制整个档案;我想你不想这样做,因为它太大了。

常用的命令行工具(tarpax)不支持将一个存档的成员复制到另一个存档。

如果您不需要保留所有权,我建议使用保险丝文件系统。您可以使用存档挂载将存档安装为文件系统;对源存档执行此操作,并在已安装的文件系统上运行 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 不同的帐户拥有,则可能无法给出预期结果。

蟒蛇的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

答案3

GNUtar确实有一个--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 ...

答案4

据我所知,该tar命令不能使用 tar 格式两个都作为输入和输出。您必须以某种方式在本地提取文件,然后再次使用 tar 来动态创建 tarfile,如下所示(这-意味着使用标准输入/输出而不是文件):

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

请注意,能够tar直接在另一个 tarfile 中提取 tarfile 是一个有趣的想法......

相关内容