如何搜索存在两个不同单词的文件?


14

我正在寻找一种搜索在同一文件中存在两个单词实例的文件的方法。到目前为止,我一直在使用以下内容进行搜索:

find . -exec grep -l "FIND ME" {} \;

我遇到的问题是,如果“ FIND”和“ ME”之间没有一个空格,则搜索结果不会产生该文件。如何在文件中同时存在单词“ FIND”和“ ME”而不是“ FIND ME”的情况下适应以前的搜索字符串?

我正在使用AIX。


1
这些单词是否存在于文件中的任何位置,或者它们始终位于同一行?
Sobrique

目的是同一行。
乍得·哈里森

另一种,如果字是在同一行是使用正则表达式与grep -E/ egrep描述你感兴趣的所有模式(和使用+,而不是;如果您发现有支持+
MattBianco

Answers:


21

使用GNU工具:

find . -type f  -exec grep -lZ FIND {} + | xargs -r0 grep -l ME

您可以标准地执行以下操作:

find . -type f -exec grep -q FIND {} \; -exec grep -l ME {} \;

但这会使每个文件运行两次。为了避免运行那么多greps并且仍然可移植,同时仍允许文件名中包含任何字符,可以执行以下操作:

convert_to_xargs() {
  sed "s/[[:blank:]\"\']/\\\\&/g" | awk '
    {
      if (NR > 1) {
        printf "%s", line
        if (!index($0, "//")) printf "\\"
        print ""
      }
      line = $0
    }'
    END { print line }'
}

find .//. -type f |
  convert_to_xargs |
  xargs grep -l FIND |
  convert_to_xargs |
  xargs grep -l ME

想法是将的输出find转换为适合xargs的格式(期望空格(SPC / TAB / NL,以及您的语言环境中的其他空格,使用的某些实现xargs),分隔单词列表,其中可以使用单引号,双引号和反斜杠逃脱空白和彼此)。

通常,您不能对的输出进行后处理find -print,因为它会用换行符分隔文件名,并且不会转义文件名中的换行符。例如,如果我们看到:

./a
./b

我们没有办法知道这是b在目录中调用的一个文件a<NL>.还是在a和中的两个文件b

通过使用.//.,因为//不能被作为输出文件路径,否则会出现find(因为没有这样的东西作为一个空的名字的目录和/文件名中是不允许的),我们知道,如果我们看到一个包含一条线//,那么这就是新文件名的第一行。因此,我们可以使用该awk命令对所有换行符进行转义,但换行符除外。

如果我们以上面的示例为例,find将在第一种情况(一个文件)中输出:

.//a
./b

哪个awk会转至:

.//a\
./b

因此,xargs将其视为一个论点。在第二种情况下(两个文件):

.//a
.//b

awk将保留原样,所以xargs看到两个参数。


为什么不使用find ... -print0grep --null代替?
razzed

@大笑,不知道你是什么意思。grep --null(aka -Z)在第一个中使用,但它是GNU扩展。-print0(另一个GNU扩展)在这里无济于事。
斯特凡Chazelas

谢谢。我想将您的Shell代码包装到一个脚本中,该脚本将搜索目录作为命令行的参数。我不太清楚这.//.意味着什么,想知道如何修改它以从命令行接受参数,比如说$1
蒂姆(Tim)

谢谢。在您的命令中,是否必须-print0find-0一起使用xargs
蒂姆(Tim)

@Tim,不确定您的意思。我没有find -print0在答案中使用任何地方。
斯特凡Chazelas

8

如果文件位于单个目录中,并且它们的名称不包含空格,制表符,换行符,或字符*?也不[-nor 开头.,则将获得包含ME的文件列表,然后将其范围缩小为还包含FIND。

grep -l FIND `grep -l ME *`

这需要更多的投票!!比“已接受”的答案优雅得多。为我工作。
roblogic

grep -l CategoryLinearAxis `grep -l labelJsFunction *`查找同时具有两个属性的文件时所做的事情。多么完美的方法。+1
WEBjuju

3

随着awk你也可以运行:

find . -type f  -exec awk 'BEGIN{cx=0; cy=0}; /FIND/{cx++}
/ME/{cy++}; END{if (cx > 0 && cy > 0) print FILENAME}' {} \;

它分别使用cxcy来计数与FIND和相匹配的行ME。在该END块中,如果两个计数器均大于0,则打印FILENAME
使用以下命令将更快/更有效gnu awk

find . -type f  -exec gawk 'BEGINFILE{cx=0; cy=0}; /FIND/{cx++}
/ME/{cy++}; ENDFILE{if (cx > 0 && cy > 0) print FILENAME}' {} +

2

或使用egrep -egrep -E类似这样:

find . -type f -exec egrep -le '(ME.*FIND|FIND.*ME)' {} \;

要么

find . -type f -exec grep -lE '(ME.*FIND|FIND.*ME)' {} +

+品牌找到(如果支持)添加多个文件(路径)名作为参数传递给命令是-exec编辑。这样可以节省进程,并且比\;为找到的每个文件调用一次命令要快得多。

-type f 仅匹配文件,以避免在目录上重复查找。

'(ME.*FIND|FIND.*ME)'是匹配包含“ ME”后跟“ FIND”或“ FIND”后跟“ ME”的任何行的正则表达式。(单引号可防止外壳程序解释特殊字符)。

-igrep命令中添加a 使其不区分大小写。

要仅匹配“ FIND”在“ ME”之前的行,请使用'FIND.*ME'

在单词之间要求空格(1个或更多,但没有其他字符): 'FIND +ME'

在单词之间留出空格(0或更多,但没有其他值): 'FIND *ME'

这些组合无穷无尽的正则表达式,并且只要您对仅一次匹配就感兴趣,egrep就非常强大。


大多数抱怨不支持“ -r”吗?这样可以消除“查找”的问题,但是在搜索的树中可能有套接字或其他非普通文件。
stolenmoment,2018年

OP使用AIX并有find疑问。
MattBianco

0

查看已接受的答案,似乎比需要的要复杂。GNU的版本find,并grepxargs支持NULL结尾的字符串。就像这样简单:

find . -type f -print0 | xargs -0 grep -l --null FIND | xargs -0 grep -l ME

您可以修改find命令以过滤到所需的文件,它可以与包含任何字符的文件名一起使用。而不增加sed解析的复杂性。如果要进一步处理文件,请--null在最后一个文件中添加另一个文件grep

find . -type f -print0 | xargs -0 grep -l --null FIND | xargs -0 grep -l --null ME | xargs -0 echo

并且,作为功能:

find_strings() {
    find . -type f -print0 | xargs -0 grep -l --null "$1" | xargs -0 grep -l "$2"
}

显然,如果您没有运行这些工具的GNU版本,请使用可接受的答案。


1
--null--print0-0都是GNU扩展。尽管它们中的一些如今已在其他实现中找到,但它们仍不可移植,并且不在POSIX或Unix标准中。
斯特凡Chazelas
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.