使用 find 和 -exec gzip 和 grep

使用 find 和 -exec gzip 和 grep

我有一堆压缩的日志文件,我想在其中搜索一个字符串。我试过这个,但没有用:

find ./ -name "*.log.zip" -exec gzip -dc {} | grep ERROR \;

它给了我:

find: incomplete statement
grep: can't open ;

我想要的是,对于每个 .log.zip 文件,将其解压缩并 grep 输出以查找“ERROR”。在 AIX 上执行此操作,看看是否值得。

答案1

您的语法有错误。Find 正在查找\;\+,但读取的是|。Grep 正在尝试打开一个名为“;”的文件。使用分号或加号终止 -exec 之间的区别在于,对所有文件运行一次命令 (+) 和为每个文件运行一次命令 (;)。

尝试这个:

find ./ -name "*.log.zip" -exec zcat {} \+ | grep ERROR
# or
find ./ -name "*.log.zip" -exec sh -c 'zcat {} | grep ERROR' \;

答案2

如果你需要知道哪些压缩日志文件包含字符串:

find ./ -name "*.log.zip" -type f -exec gzip -dc {} + | grep ERROR

如果你想知道哪些文件包含字符串:

find ./ -name "*.log.zip" -type f -exec sh -c 'gzip -dc -- "$1" | grep -q ERROR' findsh {} \; -print

第一个命令查找文件并将这些文件名传递给选项-exec。我-type f在命令中添加了限制,以确保我们只匹配文件——想象一下有人运行“mkdir foo.log.zip”。gzip将每个文件解压缩到 stdout;我们使用 删除任何findgzip错误2>/dev/null;然后整个命令的标准输出通过 管道传输grep+末尾的语法-exec将传递尽可能多的文件名,从而最大限度地减少对 的调用次数gzip。因为 gzip 将所有文件内容发送到 stdout,所以grep现在只有一个传入的字节流——没有文件名——并将打印任何匹配的行。

另一方面,如果您需要知道匹配的文件名,则必须在管道中尽早捕获它。

在 GNU/Linux 系统 (具有zgrep) 上,您可以直接执行以下操作:

find . -name "*.log.zip" -type f -exec zgrep -l ERROR {} +

这将传递(尽可能多的)文件名,zgrep然后我们要求打印匹配的文件名(`-l 选项)。

在 AIX 系统上,您可以使用一个小的 shell 脚本重新创建该功能。语法可能有点吓人,但让我们从外到内分解一下:

find ... -exec sh -c ' ... ' findsh {} \; -print

上述语句每次收集一个匹配的文件 ( \;),并将其作为参数发送给给定的sh脚本;如果脚本返回成功,则打印文件名,否则不打印。该findsh部分是任意文本;它成为$0的参数sh,为内联 shell 脚本命名。

笔记:

语法{}需要shell 脚本之外;否则,可能会导致任意命令执行。在 AIX 上,括号不会被替换里面参数-exec,因此如果您尝试这样做,您会看到“gzip:{}.gz:没有这样的文件或目录”错误。在 GNU/Linux 上,find 在 shell 脚本中替换文件名,这意味着如果有人创建了一个名为的文件$(touch foo).log.zip,你最终会得到一个名为“foo”的文件,因为 shell 脚本启动了对文件名的另一层解析。有关更多信息,请参阅此 UNIX 和 Linux 问题:可以find -exec sh -c安全使用吗?

一旦文件名被逐个传递,shell脚本将是:

gzip -dc -- "$1" | grep -q ERROR

文件名在 中$1,因此我们调用gzip -dc它。出于习惯,我尝试在任意文件名之前标记选项的结尾,以防文件名以连字符(或任何其他字符)开头,而命令可能会将其误解为选项。由于我们的find命令明确以 开始搜索./,所有匹配的文件名都将以该字符串开头,因此它们永远不会看起来像 gzip 的选项,但最好养成安全的习惯。一旦 gzip 通过管道传输内容,grep 就会悄悄地搜索字符串。如果 grep 找到字符串,shell 将成功退出,允许后续打印;否则,它将导致-exec返回错误/失败退出代码,因此不会打印文件名。

相关内容