如何将“tar”命令与“find”结合使用

max*_*max 38 linux centos find exec

find 命令给出以下输出:

[root@localhost /]# find var/log/ -iname anaconda.*
var/log/anaconda.log
var/log/anaconda.xlog
var/log/anaconda.yum.log
var/log/anaconda.syslog
var/log/anaconda.program.log
var/log/anaconda.storage.log

与 tar 结合后,它显示了这个输出:

[root@localhost /]# find var/log/ -iname anaconda.* -exec tar -cvf file.tar {} \;
var/log/anaconda.log
var/log/anaconda.xlog
var/log/anaconda.yum.log
var/log/anaconda.syslog
var/log/anaconda.program.log
var/log/anaconda.storage.log

但是在列出 tar 文件时它只显示一个文件

[root@localhost /]# tar -tvf file.tar
-rw------- root/root 208454 2012-02-27 12:01 var/log/anaconda.storage.log

我在这里做错了什么?

使用 xargs 我得到这个输出:

[root@localhost /]# find var/log/ -iname anaconda.* | xargs tar -cvf file1.tar

第二个问题

在var 前面键入/时,意味着find /var/log为什么它会给出此消息tar:从成员名称中删除前导 `/'

[root@localhost /]# find /var/log/ -iname anaconda.* -exec tar -cvf file.tar {} \;
tar:从成员名称中删除前导“/”
/var/log/anaconda.log
tar:从成员名称中删除前导“/”
/var/log/anaconda.xlog
tar:从成员名称中删除前导“/”
/var/log/anaconda.yum.log
tar:从成员名称中删除前导“/”
/var/log/anaconda.syslog
tar:从成员名称中删除前导“/”
/var/log/anaconda.program.log
tar:从成员名称中删除前导“/”
/var/log/anaconda.storage.log

以简单的形式,以下两者有什么区别?

find var/logfind /var/log

小智 53

您可以使用以下内容:

find var/log -iname 'anaconda.*' -print0 | tar -cvf somefile.tar --null -T -
Run Code Online (Sandbox Code Playgroud)

-print0-T工作在一起,以允许用空格换行符等文件名的最后-告诉tar从stdin读取输入文件名。

请注意,-print0必须出现在您的陈述末尾,根据此答案。否则,您可能会获得比预期更多的文件。

  • 如果你使用 find 的 `-print0` 选项,你还需要 tar 的 `--null` 选项。 (9认同)
  • 你省略了 `-name` 选项,导致你的解决方案对整个目录进行 `tar`。如果这就是你想要的,你可以更容易地用 `tar -cvf file.tar var/log` 来完成,根本不使用 `find`。 (2认同)
  • +1 将列表传送到 `tar` 是一个好主意。如果您希望路径名可能有空格,这绝对是最好的解决方案。我什至会把它描述为技术上最好的,因为它既可靠又高效。但是它需要额外的关于 `find` 和 `tar` 的特殊知识。我更喜欢命令替换几乎只是因为它是一个更通用的工具:学习如何使用它一次,然后在任何地方使用它。(但我承认,我在 Windows 上使用它总是可以工作的外壳。)如果我看起来很粗鲁,我深表歉意。 (2认同)
  • 您已经获得了 +1。要开心。:) 长命令行总是在任何操作系统上创建进程的祸根。我记得在 90 年代初与微软的 [Mark Lucovsky](http://en.wikipedia.org/wiki/Mark_Lucovsky) 争论说他们在 NT 上的 32K Unicode 字符限制太小,让他抱怨我不知道有多少将长度存储为 long 而不是 short 在内核中的任何地方都需要更多字节。叹。当 arg 列表太长时,更一般的解决方案是在 shell 中做更多的事情(如果可能;在我的情况下是这样)或使用 `xargs`。 (2认同)
  • 并且`--no-unquote` 也被证明是需要的:否则包含反斜杠的文件名将被错误处理。(不,这不是假设——我真的从别人的代码创建了一个 tar 存档,其中包含一个文件名,名称中带有反斜杠,这就是我发现的。) (2认同)
  • @hvd 那么你应该找到他们并给他们一个很好的踢。 (2认同)

slh*_*hck 43

注意:有关更有效的解决方案,请参阅@Iain 的回答

请注意,find它将为其找到的每个文件调用该-exec操作。

如果您tar -cvf file.tar {}为每个文件find输出运行,这意味着您file.tar每次都会覆盖,这解释了为什么您最终只剩下一个存档anaconda.storage.log- 它是最后一个文件find输出。

现在,您实际上希望文件附加到存档中,而不是每次都创建它(这就是该-c选项的作用)。因此,请使用以下内容:

find var/log/ -iname "anaconda.*" -exec tar -rvf file.tar {} \;
Run Code Online (Sandbox Code Playgroud)

-r选项会附加到存档中,而不是每次都重新创建。

注意:替换-iname anaconda.*-iname "anaconda.*". 星号是一个通配符,甚至可以在你看到它之前被你的 shell扩展find。为防止这种扩展,请将参数用双引号括起来。


至于tar删除前导/:存档应该只包含相对文件名。例如,如果您添加带有前导 的文件/,它们将被存储为绝对文件名,字面意思/var/…是您的计算机。

IIRC 这只是对tarGNU 以外的实现的预防措施,这种方式更安全,因为/var/…如果提取存档时包含相关文件名,则不会覆盖其中的实际数据。

  • 但是请注意,如果您尝试以这种方式对实际的磁带存档进行“tar”处理,一次添加一个文件,倒带磁带,然后每次都重新阅读整个内容以到达结尾,则整个过程会慢得离谱。如果您将 tar 文件写入磁盘,您的解决方案**仅**适用。 (6认同)
  • 您可以使用 `{} +` 代替 `{} \;` 这样它会将 find 的结果分组为一个参数 (3认同)
  • 是的,但我认为我们可以放心地无视这种情况;) (2认同)

Nic*_*ton 13

尝试这个:

tar -cvf file.tar `find var/log/ -iname "anaconda.*"`
Run Code Online (Sandbox Code Playgroud)

您试图使用findto -exec tar。但是该-exec选项的工作方式是,它为找到的每个匹配文件运行一次该命令,从而导致tar每次都覆盖它生成的 tar 文件。这就是为什么你只得到了最后一个。此外,您需要在指定的模式周围加上引号,find以便 shell 在将其传递给find.

使用带反引号的命令替换(或使用$(...)符号,如果您愿意),由 产生的整个名称列表将find作为参数粘贴回命令行tar,使其一次性写入所有名称。

  • @slhck 这就是为什么 Unix 和 Linux 中的文件和目录名称传统上避免在名称中使用空格的原因。这也是为什么在 Windows 上,名称带有空格很常见的原因,我使用处理整行的双反引号向我自己的 [Hamilton C shell](http://hamiltonlabs.com/cshell.htm) 添加了一个额外的命令替换符号(可能包括空格)作为要粘贴回命令行的单个单词。不幸的是,没有一个 Unix shell 具有该功能。 (4认同)
  • @slhck,从 find 管道标准输出实际上通常是一个好主意,正如您在评论中链接到的页面中非常清楚地解释的那样:)。事实上,这是推荐的做事方式。你应该像我在回答中那样使用一些技巧(例如 `-print0` 的 `read -r`)。 (3认同)
  • 如果找到名称中带有空格、换行符或通配符的输出文件,结果可能会很糟糕。这肯定会失败——从`find` 管道标准输出很少是一个好主意。http://mywiki.wooledge.org/ParsingLs (2认同)
  • 不,但在其他地方很可能会发生。这就是为什么防御性编程是个好主意的原因——安全总比后悔好。此外,发现此问题的访问者可能不一定有完全相同的问题,并想知道为什么他们在这里找到的命令似乎适用于这种情况,但对他们来说却失败了。我会让你来修复命令,我只是认为提及它很重要,因为很多人迟早会遇到这个问题。 (2认同)

ter*_*don 6

问题 1

您的命令失败,因为tar正在获取找到的每个文件并将它们归档到file.tar. 每次这样做时,它都会覆盖之前创建的file.tar.

如果您想要的是一个包含所有文件的存档,那么只需tar直接运行,就不需要find(是的,这适用于名称中带有空格的文件):

tar -vcf file.tar /var/log/anaconda*   
Run Code Online (Sandbox Code Playgroud)

问题2

这两个命令完全不同:

  • find var/log 将搜索一个名为的var/log 目录,它是您当前目录的子目录,它相当于find ./var/log(注意./)。

  • find /var/log 将搜索一个名为的目录/var/log ,它是根目录/.

主要/信息来自tar,而不是find。这意味着它正在删除/您的第一个文件名以使绝对路径成为相对路径。这意味着当您解压缩存档时,文件 from/var/log/anaconda.error将被提取到./var/log/anaconda.error