我有一堆压缩的日志文件,我想在其中搜索一个字符串。我试过这个,但没有用:
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;我们使用 删除任何find
或gzip
错误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
返回错误/失败退出代码,因此不会打印文件名。