如何将“ tar”命令与“ find”组合


31

find命令给出以下输出:

[root @ localhost /]#查找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 /]#查找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 -------根/根208454 2012-02-27 12:01 var / log / anaconda.storage.log

我在这里做错了什么?

使用xargs我得到以下输出:

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

第二个问题

在var前面键入/时,表示find /var/log为什么要赋予此Mesaage tar:从成员名称中删除前导“ /”

[root @ localhost /]#查找/ 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


这是准主题词,但继续执行该find命令时,应引用搜索词。它有时但并非总是如此工作。
nerdwaller 2012年

1
如果使用{} +代替,{} \;它将把find的结果分组为一个参数
Jason S'S

Answers:


39

注意:请参阅@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 {} \;

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

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


至于tar删除前导/:存档中应仅包含相对文件名。如果添加的文件带有前导/,它们将作为绝对文件名存储/var/…,例如,在字面上意味着您的计算机。

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


6
但是请注意,如果您尝试tar通过这种方式访问​​实际的磁带存档,一次添加一个文件,倒带,然后每次都重新读取整个内容直到结束,那么整个过程将非常缓慢。您的解决方案在将tar文件写入磁盘时才适用。
妮可·汉密尔顿

2
没错,但我认为我们可以放心地忽略这种情况;)
slhck 2012年

@slhck *是通配符,应该与所有可能的值匹配吗?但是这里find /var/log/ -iname anaconda*什么也没有find /var/log/ -iname anaconda.*给出,却给出了输出,为什么呢?
最大

使用通配符后,将不再可见find。所以,如果你有anaconda*,并在当前文件夹里面的东西命名,例如,anaconda5(匹配这个通配符),通配符将扩大,并且find将看到-iname anaconda5的不是-iname anaconda*。为什么第一个不起作用而第二个不起作用取决于您当前目录中的文件。@max
slhck

2
您可以使用{} +代替,{} \;以便将find的结果分组为一个参数
Jason S'S

41

您可以使用类似:

find var/log -iname 'anaconda.*' -print0 | tar -cvf somefile.tar --null -T -

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

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


2
您省略了该-name选项,从而导致tar对整个目录的解决方案。如果这就是您想要的,那么您可以更轻松地完成它,甚至根本tar -cvf file.tar var/log不需要使用它find
妮可·汉密尔顿

2
+1将清单传送到tar是一个好主意。如果您期望路径名包含空格,则绝对是最佳解决方案。从技术上讲,我什至将其描述为最佳,因为它既可靠又高效。但这需要同时具有find和的其他特殊知识tar。我之所以更喜欢使用命令替换,是因为它是一种更通用的工具:学习一次使用它,然后在任何地方使用它。(但是,我承认,我在Windows上具有始终可以正常运行的外壳。)如果我看起来很粗鲁,则表示歉意。
妮可·汉密尔顿

2
您已经获得了+1。要开心。:)在任何OS上,长命令行始终是进程创建i / f的祸根。我记得在90年代初与Microsoft的Mark Lucovsky争论过,他们在NT上使用的32K Unicode字符限制太小,让他抱怨我不知道在内核中各处存储多长而不是短的长度还需要多少字节。 。叹。当arg列表太长时,更一般的解决方案是在shell中执行更多操作(如果可能;在我的情况下)或使用xargs
妮可·汉密尔顿

9
如果使用find的-print0选项,则还需要tar的--null选项。
mivk 2014年

2
而且--no-unquote事实证明也是需要的:否则包含反斜杠的文件名将被错误处理。(不,这不是一个假设-我实际上是从别人的代码创建tar归档文件,其中包含名称中带有反斜杠的文件名,这就是我的发现方式。)
hvd

12

尝试这个:

tar -cvf file.tar `find var/log/ -iname "anaconda.*"`

你试图使用find-exec tar。但是该-exec选项的工作方式是,它为找到的每个匹配文件运行一次该命令,从而tar覆盖每次生成的tar文件。因此,您只剩下最后一个。另外,您需要在指定的模式find前后加上引号,以使外壳程序在将其传递给之前不会对其进行扩展find

使用带有反引号的命令替换(或使用$(...)符号,如果需要的话),将由产生的名称的整个列表find作为的参数粘贴回命令行tar,从而使它们立即全部写入。


2
如果find输出文件的名称,换行符或通配符中带有空格,则可能导致不良后果。这注定会失败–从管道标准输出find很少是一个好主意。mywiki.wooledge.org/ParsingLs
slhck

3
@slhck,从find传递stdout实际上通常是一个好主意,正如您在评论中链接到的页面中非常清楚地解释的:)。实际上,这是推荐的处理方式。您应该像在回答中一样使用一些技巧(例如read -rof -print0)。
terdon

4
@slhck这就是为什么在Unix和Linux中,文件和目录名通常在名称中避免使用空格。这也是为什么在Windows中,带有空格的名称很常见的原因,我在我自己的Hamilton C shell中使用双反引号添加了一个附加的命令替换符号,该双反引号将整行(可能包括空格)作为单个单词粘贴到命令中线。不幸的是,没有Unix shell具有该功能。
妮可·汉密尔顿

1
他们可能传统上避免使用它,但是通过GUI在用户空间中创建文件后,您就不能再忽略具有空格的文件并将它们视为二等公民了(因为它是Unix)。很高兴将其包含在外壳程序中,但是它适用于Windows,如果您仅使用正确的语法并采取适当的预防措施,Unix外壳程序就不需要该功能。这就是为什么我首先发表评论的原因。
slhck

2
不,但是在其他地方,这很可能会发生。这就是为什么进行防御性编程是一个好主意–最好还是要后悔。此外,发现此问题的访问者可能不一定有完全相同的问题,并且想知道为什么他们在此找到的命令似乎适用于这种情况,但对他们却失败了。我将由您自己来修复命令,我只是认为提到它很重要,因为很多人迟早会遇到此问题。
slhck

6

问题1

您的命令失败,原因tar是将找到的每个文件都存档并将其归档到中file.tar。每次这样做都会覆盖先前创建的file.tar

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

tar -vcf file.tar /var/log/anaconda*   

问题2

这两个命令完全不同:

  • find var / log将搜索一个名为的var/log 目录,该目录当前目录的子目录,它等效于find ./var/log(请注意./)。

  • find / var / log将搜索名为/var/log 的目录,该目录是根/目录的子目录

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


1

有两种方法-exec可以起作用。一种方法可以多次运行命令-每个文件一次;另一种方法是一次运行命令,包括将所有文件作为参数列表。

  • -exec tar -cvf file.tar {} ';'tar对每个文件运行命令,每次都覆盖存档。
  • -exec tar -cvf file.tar {} '+'运行tar一次命令,为找到的所有文件创建档案。

1

我认为如果文件很多,对每个文件使用-exec会使tar压缩非常慢。我更喜欢使用命令:

find . -iname "*.jpg" | cpio -ov -H tar -F jpgs.tar

直到它开始失败/bin/cpio: xxx: Cannot open: Too many open files
SYN
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.