我需要找到不包含一个字符串(例如)但包含另一个字符串(例如)的.php
文件。.pl
aaa
bbb
我目前正在使用这个命令:
find /path/ \( -iname '*.php*' -or -name '*.pl*' \) -exec sh -c 'grep -l -v "aaa" {} | grep -l "bbb" {}' \; > resulttofile
大约有 50 万个文件需要搜索,所以我想知道,
- 如果我的命令正常工作 - 一些眼睛采样会给出积极的结果,
- 如果可以使用某种其他形式变得更快(目前在虚拟机上大约需要 2 分钟,但将添加更多文件),或者使用
awk
orsed
代替grep
- 或者可能只是一种组合grep
而不是两种。
该系统是 Debian GNU/Linux。
答案1
您的命令无法正常工作:第一个命令grep
将列出包含不匹配行的任何文件"aaa"
,第二个命令grep
将忽略第一个命令的输出,因为它给出了自己的要处理的文件 - 因此您将获得匹配的文件列表"bbb"
,无论如何它们是否包含"aaa"
。您需要要求grep
仅在不包含任何行匹配"aaa"
( grep -L
) 的情况下列出文件,并使用xargs
处理结果文件列表并仅将其提供给第二个文件grep
(或使第二个grep
文件以第一)。
最重要的是,只有find
列出的文件名不会给 shell 带来问题,它才会起作用 - 特别是,{}
直接包含在给定的命令中sh -c
意味着文件名最终会被解释为 shell 命令(请参阅是否可以安全地使用“find -exec sh -c”?了解详情)。
假设您使用的是 GNU ,以下内容将需要更少的grep
调用并且更安全:grep
find /path/ \( -iname '*.php*' -o -name '*.pl*' \) -exec grep -LZ aaa {} + |
xargs -r0 grep -l bbb
这-or
运算符是 GNU 的扩展find
。使用-o
为了便携性。
答案2
未经测试,但这应该做我认为你想要的,使用 GNU awk fornextfile
和ENDFILE
find /path/ \( -iname '*.php*' -or -name '*.pl*' \) -exec awk '
/aaa/{a=1} /bbb/{b=1} a&&b{nextfile} ENDFILE{if (b && !a) print FILENAME; a=b=0}
' {} + > resulttofile
上面一次只对多个文件调用 awk 一次,因此应该是高效的。
上面是如何通常匹配文件中的多个模式,然后在完全读取文件后评估匹配组合的结果,但如下@G-Man 说“恢复莫妮卡”中提到的一条评论在这种特定情况下,您可以通过在匹配时停止读取当前文件来提高效率,aaa
因为成功标准是aaa
不存在:
/aaa/{a=1; nextfile} /bbb/{b=1} ENDFILE{if (b && !a) print FILENAME; a=b=0}
答案3
您可以将多个 -exec 指令(或其他指令)与一个 find 命令链接在一起:
find /path \( -iname '*.php*' -or -name '*.pl*' \) -exec grep -q "bbb" {} ";" \
-exec grep -L "aaa" {} ";" > resulttofile
(换行只是为了适应 SE 的布局)。