查找包含多个字符串的文件

11

我使用一个命令来递归查找包含特定 string1 的文件:

find . -type f -exec grep -H string1 {} \;

我需要查找包含多个字符串的文件,因此命令应该返回包含所有字符串的文件。类似这样:
find . -type f -exec grep -H string1 AND string2 {} \;

我找不到解决方法。这些字符串可能出现在文件的任何地方。即使是只针对两个字符串的解决方案也很好。


我可以将您的问题转化为一个“egrep”问题:命令egrep -l“string1|string2”会给出包含string1string2的所有文件,如果存在参数,则可以使用egrep -l“string1<parameter>string2”来查找包含string1string2的文件,这样就可以解决您的问题。(虽然我不知道是否存在这样的参数) - Dominique
理论上可以有一个类似于 | 的并集运算符 &,但是没有常见的正则表达式工具实现它。简单的解决方法是使用 awk '/pattern1/ && /pattern2/',因此已经有一种简单的方法来实现这一点,尽管不能使用 grep - tripleee
6个回答

15

你也可以尝试这个;

find . -type f -exec grep -l 'string1' {} \; | xargs grep -l 'string2'

这显示包含字符串1和字符串2的文件名。


2
这是一个非常好的解决方案。我建议用单引号将 {} 包围起来,并使用 -d 参数来避免因带空格的文件名而出现错误,就像这样:find . -type f -exec grep -l 'string1' '{}' \; | xargs -d '\n' grep -l 'string2' - snaut
2
我还建议使用 + 而不是 \; 结束 find 命令的 -exec 选项。这将使用单个 grep 命令尽可能多地搜索文件,减少运行的进程数量,从而获得更快的结果。 - Greg Barrett

6
你可以链接你的操作并使用第一个操作的退出状态来执行第二个操作,仅当第一个操作成功时才执行第二个操作。 (省略原始之间的运算符将默认为 -and / -a 。)
find . -type f -exec grep -q 'string1' {} \; -exec grep -H 'string2' {} \;

第一个 grep 命令使用了 -q,也就是“安静”模式,如果找到了字符串就返回成功的退出状态。
为了一次性使用 grep 收集所有包含 string1 的文件,并查找 string2,可以使用 -exec ... {} +
find . -type f -exec grep -q 'string1' {} \; -exec grep 'string2' {} +

4
使用GNU grep
grep -rlZ 'string1' | xargs -0 grep -l 'string2'


来自 man grep

-r, --recursive

递归读取每个目录下的所有文件,只有在命令行上指定了符号链接时才会跟随它们。请注意,如果未给出任何文件操作数,则grep搜索当前工作目录。这相当于-d递归选项。

-Z, --null 输出一个零字节(ASCII NUL字符),而不是通常跟随文件名的字符。例如,grep-lZ在每个文件名后输出一个零字节,而不是通常的换行符。此选项使输出明确无误,即使存在包含像换行符这样的不寻常字符的文件名称也是如此。此选项可与类似find-print0、perl-0、sort-z和xargs-0的命令一起使用,以处理任意文件名,甚至包含换行符的文件名。


3

很惊讶这个老问题缺乏明显简单的Awk解决方案:

find . -type f -exec awk '/string1/ && /string2/ { print; r=1 } END { exit 1-r }' {} \;

trickery with the r variable 是为了模拟 grep 的退出码(零表示找到,一表示未找到;如果您不关心,可以省略)。为了效率,可以从 -exec ... {} \; 切换到 -exec ... {} +,尽管这样可能需要对 Awk 脚本进行重构(要么丢弃退出码,要么更改退出码以指示“没有匹配的文件”vs“只有一些文件匹配”vs“所有文件匹配”?)。以上代码查找同时包含两个字符串的文件。在任何行上找到它们的情况很容易改变。
awk '/string1/ { s1=1 }
  /string2/ { s2=1 }
  s1 && s2 { print FILENAME; exit }
  END { exit(1 - (s1 && s2)) }' file

这只是打印文件名,并假设您只有一个输入文件。为了处理多个文件,请稍微重构一下,当访问新文件时重新设置s1s2的值:

awk 'FNR == 1 { s1 = s2 = 0 }
  /string1/ { s1 = 1 }
  /string2/ { s2 = 1 }
  s1 && s2 { r=1; print FILENAME; nextfile }
  END { exit 1-r }' file1 file2 file3 ...

一些古老的Awk版本可能不支持nextfile,但现在它已经成为POSIX标准。


0

答案

从本页面的其他答案中可以看出,有几个命令行工具可用于在文件之间执行联合搜索。一个快速而灵活的解决方案是使用ag

ag -l string1 | xargs ag -l string2

有用的变体

若要进行不区分大小写的搜索,请使用ag-i选项:

ag -il string1 | xargs ag -il string2

为了获得更多的搜索结果,可以扩展管道:

ag -l string1 | xargs ag -l string2 | xargs ag -l string3 | xargs ag -l string4

0
grep -rlZ string1 | xargs -0 grep -l string2

如果您的模式是固定字符串,我们可以通过在grep命令中添加-F来加速处理:
grep -rlZF string1 | xargs -0 grep -lF string2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接