Regex 如何查找文件的*不同*部分中可能包含str1和str2的文件名

Regex 如何查找文件的*不同*部分中可能包含str1和str2的文件名,regex,grep,Regex,Grep,我试图找到一种方法来确定文件的名称,这些文件可能在文件的不同部分中包含str1和str2。 grep str1 | grep str2将不工作,因为grep str2将在包含str1的行上运行。 我可以得到一个包含str1的文件列表,再得到一个包含str2的文件列表,然后寻找交叉点,但效率很低。 一种更有效的方法是让grepstr1输出一个文件列表,然后让grepstr2对其进行操作,但这意味着grep将一次又一次地打开、读取和关闭同一个文件。 也许最理想的方法是打开一个文件,grep for

我试图找到一种方法来确定文件的名称,这些文件可能在文件的不同部分中包含str1和str2。 grep str1 | grep str2将不工作,因为grep str2将在包含str1的行上运行。 我可以得到一个包含str1的文件列表,再得到一个包含str2的文件列表,然后寻找交叉点,但效率很低。 一种更有效的方法是让grepstr1输出一个文件列表,然后让grepstr2对其进行操作,但这意味着grep将一次又一次地打开、读取和关闭同一个文件。 也许最理想的方法是打开一个文件,grep for str1和str2,然后确定该文件是否同时包含str1和str2,但我无法创建这样的命令。 我想它应该和这个发现很相似-name*-exec grep str1&&grep str2{};但这在语法上是不正确的

您对

让grepstr1输出一个文件列表,然后让grepstr2对其进行操作

解决方案使它听起来比实际情况更糟糕:每个文件都将由第一个grep至少打开一次,并且每个确实包含str1的文件都将由第二个grep第二次打开。没那么糟糕,当然也不是“一次又一次地打开、读取和关闭同一个文件”。最多两次,有时只有一次。如果您想要更高的效率,我可能会给您一个基于perl的答案,但这可能不值得麻烦。实际上,打开一个文件两次有困难吗

打开某些文件两次的解决方案 grep的-l参数只提供文件名。因此grep-lstr1文件…将列出所有包含str1的文件。然后用str2重复。结果将类似于:

grep -l str1 files… | xargs grep -l str2 /dev/null
结尾的/dev/null是一个技巧,因此如果没有与str1匹配的文件,那么第二个grep就不会在stdin上读取

如果您使用的是GNU grep和findutils,那么使用grep和xargs的-Z和-0选项将更安全


如果str1出现在str2之前,那么您可以使用

find . -name "str1*str2"

尝试使用类似于str1.*str2 | str2.*str1的regexp。我不确定,可能您需要使用egrep而不是grep,我认为awk更适合此任务。这里有一种方法可以做到:

awk -v str1="$str1" -v str2="$str2" '
  FNR == 1 { m1 = m2 = 0 }
  index($0, str1) { m1 = 1 }
  index($0, str2) { m2 = 1 }
  m1 && m2 { print FILENAME; nextfile }' file1 file2 filen
这假设$str1和$str2被设置为要搜索的字符串。解决方案在一次过程中进行匹配,并在找到两个字符串后立即退出

稍微优化的版本:

awk -v str1="$str1" -v str2="$str2" '
  FNR == 1 { m1 = m2 = 0 }
  !m1 && index($0, str1) { m1 = 1 }
  !m2 && index($0, str2) { m2 = 1 }
  m1 && m2 { print FILENAME; nextfile }' file1 file2 filen
更新: 在下面的评论中添加了Ed Morton提到的错误修复和优化。还请注意,在awk的旧版本中,nextfile语句可能存在一些可移植性问题,请参阅中有关该主题的讨论。nextfile语句已经出现在POSIX标准中,因此将来应该可以更广泛地使用

使用GNU awk目瞪口呆:

awk -v RS='\0' -v str1="$str1" -v str2="$str2" '
   index($0,str1) && index($0,str2) { print FILENAME; nextfile }
' file1 file2 filen
对于任何awk:

awk -v str1="$str1" -v str2="$str2" '
   FNR == 1             { found[1] = found[2] = 0 }
   index($0,str1)       { found[1]++ }
   index($0,str2)       { found[2]++ }
   found[1] && found[2] { files[FILENAME] }
   END { for (file in files) print file }
' file1 file2 filen

当str1和str2不在同一行时,这将不起作用。@pstr:If think grep-Ezl'str1.*str2 | str2.*str1'是OP的目标。在此解决方案中,每个带有str1的文件将打开两次。作者希望每个文件只打开和关闭一个。-1这不是关于名称匹配的文件,而是关于内容匹配的文件。退出使一次无法处理多个匹配的文件。重构它应该不会太难,这样它就可以处理一个文件参数列表,而不仅仅是一个文件参数。@tripleee:当然,我应该使用nextfile,很快就会添加它。请注意,当您使用nextfile时,您只会使它成为GNU awk。另外,你要做的是匹配一个re,而不是一个字符串,所以如果str1包含一个与abc匹配的a.c,等等。不仅仅是字面的a.c-你应该使用index而不是match或~。上面的内容还需要重置m1和m2,或者一旦它们第一次被设置,它们将在其余的文件中保持设置。@EdMorton:对,我对重新匹配也不满意,只是没有想到索引函数,谢谢;当然,需要重置匹配标志。nextfile受当前版本的gawk和nawk的支持,至少根据,它将在mawk版本1.4中,但它不是完全可移植的;这个
awk -v str1="$str1" -v str2="$str2" '
   FNR == 1             { found[1] = found[2] = 0 }
   index($0,str1)       { found[1]++ }
   index($0,str2)       { found[2]++ }
   found[1] && found[2] { files[FILENAME] }
   END { for (file in files) print file }
' file1 file2 filen