我需要找到满足相对复杂条件的文件。例如,我想查找满足以下所有条件的所有文件:
- 包含单词 AAAA
- 包含单词 BBB 或 CCCCC(可能两者都包含)
- 不包含单词 DDD
这些单词可以以任何顺序出现,并且可以出现在不同的行(或同一行)中。
我有一个解决方案,它结合了find
和egrep
,但它不是很清晰。
find . \( -type f -and -exec egrep -q 'BBB|CCCCC' {} \; \
-and -exec egrep -q AAAA {} \; \
-and -not -exec egrep -q DDD {} \; \) -print
有没有更好的方法来解决这个问题?
答案1
在我看来,您的解决方案对于该任务来说非常清晰。然而,它很慢,因为它为每个文件生成 3 个进程。我认为 Awk 更适合这里,因为它允许一次读取整批文件(如 ARG_MAX 允许的),使用{} +
而不是{} \;
.
GNU awk:
find . -type f -exec gawk '
BEGINFILE{c1=c2=c3=0}
/AAA/ {c1=1}
/BBB/||/CCC/{c2=1}
/DDD/ {c3=1; nextfile}
ENDFILE{if(c1 && c2 && !c3)print FILENAME}
' {} +
POSIX *:
find . -type f -exec awk '
FNR==1{
if(NR>1 && c1 && c2 && !c3)print f
c1=c2=c3=0
f=FILENAME
}
/AAA/ {c1=1}
/BBB/||/CCC/{c2=1}
/DDD/ {c3=1; nextfile}
END{if(c1 && c2 && !c3)print f}
' {} +
*实际上,nextfile
仍然不是 POSIX 但是已被下一期标准接受。您可以将其删除以符合 POSIX Issue 7 合规性;结果是一样的,但性能会受到影响。
笔记:如果 awk 没有读取文件的权限,它就会退出。在 GNU Find 中,只需添加该-readable
标志即可避免这种情况。如果 GNU Find 不可用,Test 可以用作附加过滤器:
find . -type f -exec test -r {} \; -exec awk '
...
' {} +
但是为每个文件生成一个测试会带来性能损失。
进一步阅读:
答案2
这是另一种方法。它的工作原理是首先创建包含每个单词的文件列表:
find . -type f -exec grep -lF 'AAA' {} + > files_with_AAA
find . -type f -exec grep -lF 'BBB' {} + > files_with_BBB
...
然后可以通过处理列表来实现条件:
grep -xFf files_with_BBB files_with_AAA # AAA & BBB
grep -xFvf files_with_BBB files_with_AAA # AAA & ~BBB
sort -u files_with_AAA files_with_BBB # AAA | BBB
如果有大量文件和多个表达式需要计算,则速度会更快,因为不必重新扫描每个文件。
答案3
我有兴趣查看与这些解决方案的时间比较,特别是对于ripgrep,它具有内置的并行处理功能。
和
GNU grep
grep -rLZ 'DDD' | xargs -0 grep -lZ 'AAAA' | xargs -0 grep -lE 'BBB|CCCCC' # if your search terms are literal strings grep -rLZF 'DDD' | xargs -0 grep -lZF 'AAAA' | xargs -0 grep -lF -e 'BBB' -e 'CCCCC'
和
rg
。请注意,默认情况下启用递归搜索,并且默认情况下也会忽略某些文件。-u
如果您不希望此类文件.gitignore
影响结果,则需要使用。用于-uu
额外搜索隐藏文件。-uuu
如果您还想搜索二进制文件,请使用 。rg --files-without-match -0 'DDD' | xargs -0 rg -l0 'AAAA' | xargs -0 rg -l 'BBB|CCCCC' # if your search terms are literal strings rg --files-without-match -0F 'DDD' | xargs -0 rg -l0F 'AAAA' | xargs -0 rg -lF -e 'BBB' -e 'CCCCC'
具有
rg
多行匹配rg -lUP '(?s)\A(?!.*DDD)(?=.*(BBB|CCCCC)).*AAAA'