如何在文件的*不同*部分中查找具有str1和str2的文件的名称

时间:2013-01-05 14:16:29

标签: regex grep

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

5 个答案:

答案 0 :(得分:2)

使用GNU awk(gawk):

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

答案 1 :(得分:0)

您对

的评论
  

使grep str1输出一个文件列表,然后让grep str2对该文件进行操作

解决方案让它听起来比实际情况要糟糕得多:每个文件至少会被第一个grep打开一次, 包含str1的每个文件都会再打开一次到第二个grep。不是那么糟糕,当然不是“一遍又一遍地打开,阅读和关闭同一个文件”。最多两次,有时只一次。如果你的效率更高,我可以根据perl给你一个答案,但这可能不值得。你实际上两次打开文件有困难吗?

打开一些文件两次的解决方案

-l的{​​{1}}参数为您提供文件的名称。因此grep将列出内部某处grep -l str1 files…的所有文件。然后用str1重复一遍。结果将是:

str2

最后的grep -l str1 files… | xargs grep -l str2 /dev/null 是一个技巧,因此如果没有文件与/dev/null匹配,那么第二个str1不会卡在grep上1}}。

如果您使用的是GNU grep和findutils,那么使用stdin-Z选项-0grep可以更安全。

xargs

答案 2 :(得分:0)

我认为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语句可能存在一些可移植性问题,请参阅GNU awk appendix B中有关该主题的讨论。 POSIX标准中nextfile语句已accepted for inclusion,因此将来应更广泛地使用。

答案 3 :(得分:-1)

尝试使用(str1.*str2|str2.*str1)之类的正则表达式。我不确定,您可能需要使用egrep代替grep

答案 4 :(得分:-2)

如果str1发生在str2之前,那么你可以使用

find . -name "str1*str2"