检查文件中是否存在多个字符串或正则表达式

cod*_*ter 18 bash search grep

我想检查我的所有字符串是否存在于文本文件中.它们可以存在于同一条线上或不同的线上.部分匹配应该没问题.像这样:

...
string1
...
string2
...
string3
...
string1 string2
...
string1 string2 string3
...
string3 string1 string2
...
string2 string3
... and so on
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,我们可以用正则表达式代替字符串.

例如,以下代码检查文件中是否存在任何字符串:

if grep -EFq "string1|string2|string3" file; then
  # there is at least one match
fi
Run Code Online (Sandbox Code Playgroud)

如何检查是否所有这些都存在?由于我们只对所有匹配项的存在感兴趣,因此我们应该在所有字符串匹配后立即停止读取文件.

是否有可能做到这一点,而不必调用grep多次(不会标尺时输入文件较大,或者如果我们有一个大量的字符串相匹配),或使用工具,如awkpython

此外,是否有一个字符串的解决方案可以很容易地扩展为正则表达式?

Ed *_*ton 18

awk是发明grep,shell等人发明的工具来做这样的一般文本操作工作所以不确定为什么你想要试图避免它.

如果简洁是您正在寻找的,这里是GNU awk单行程,可以满足您的要求:

awk 'NR==FNR{a[$0];next} {for(s in a) if(!index($0,s)) exit 1}' strings RS='^$' file
Run Code Online (Sandbox Code Playgroud)

这里有一堆其他信息和选项:

假设你真的在寻找字符串,那就是:

awk -v strings='string1 string2 string3' '
BEGIN {
    numStrings = split(strings,tmp)
    for (i in tmp) strs[tmp[i]]
}
numStrings == 0 { exit }
{
    for (str in strs) {
        if ( index($0,str) ) {
            delete strs[str]
            numStrings--
        }
    }
}
END { exit (numStrings ? 1 : 0) }
' file
Run Code Online (Sandbox Code Playgroud)

一旦所有字符串匹配,上述内容将立即停止读取文件.

如果您正在寻找regexps而不是字符串,那么使用GNU awk进行多字符RS并在END部分保留$ 0,您可以:

awk -v RS='^$' 'END{exit !(/regexp1/ && /regexp2/ && /regexp3/)}' file
Run Code Online (Sandbox Code Playgroud)

实际上,即使它是字符串你也可以做:

awk -v RS='^$' 'END{exit !(index($0,"string1") && index($0,"string2") && index($0,"string3"))}' file
Run Code Online (Sandbox Code Playgroud)

上面两个GNU awk解决方案的主要问题是,像@ anubhava的GNU grep -P解决方案一样,整个文件必须一次读入内存,而上面的第一个awk脚本,它可以在任何awk中工作任何UNIX机器上的任何shell,一次只存储一行输入.

我看到你在你的问题下添加了一条评论,说你可能有几千个"模式".假设你的意思是"字符串",那么不是将它们作为参数传递给脚本,而是可以从文件中读取它们,例如使用GNU awk进行多字符RS和每行一个搜索字符串的文件:

awk '
NR==FNR { strings[$0]; next }
{
    for (string in strings)
        if ( !index($0,string) )
            exit 1
}
' file_of_strings RS='^$' file_to_be_searched
Run Code Online (Sandbox Code Playgroud)

对于正则表达式它是:

awk '
NR==FNR { regexps[$0]; next }
{
    for (regexp in regexps)
        if ( $0 !~ regexp )
            exit 1
}
' file_of_regexps RS='^$' file_to_be_searched
Run Code Online (Sandbox Code Playgroud)

如果您没有GNU awk并且您的输入文件不包含NUL字符,那么您可以通过使用RS='\0'而不是RS='^$'或通过在读取时一次附加一行变量来获得与上面相同的效果,然后处理该变量结束部分.

如果你的file_to_be_searched太大而不适合内存那么它就是字符串:

awk '
NR==FNR { strings[$0]; numStrings=NR; next }
numStrings == 0 { exit }
{
    for (string in strings) {
        if ( index($0,string) ) {
            delete strings[string]
            numStrings--
        }
    }
}
END { exit (numStrings ? 1 : 0) }
' file_of_strings file_to_be_searched
Run Code Online (Sandbox Code Playgroud)

和regexps的等价物:

awk '
NR==FNR { regexps[$0]; numRegexps=NR; next }
numRegexps == 0 { exit }
{
    for (regexp in regexps) {
        if ( $0 ~ regexp ) {
            delete regexps[regexp]
            numRegexps--
        }
    }
}
END { exit (numRegexps ? 1 : 0) }
' file_of_regexps file_to_be_searched
Run Code Online (Sandbox Code Playgroud)

  • @EdMorton非常好的答案!我只缺少一件事,如果搜索到的字符串被换行符分割,该怎么办?想象一本教科书,你要搜索的字符串被换行符分开.这有点困难,但它可能是这个答案的一个很好的补充! (4认同)
  • 这是一个很好的答案。开始悬赏以吸引更多关注。 (2认同)

ken*_*orb 9

git grep

以下是使用git grep多种模式的语法:

git grep --all-match --no-index -l -e string1 -e string2 -e string3 file
Run Code Online (Sandbox Code Playgroud)

您还可以将模式与布尔表达式组合,例如--and,--or--not.

检查man git-grep帮助.


--all-match在给出多个模式表达式时,指定此标志以将匹配限制为具有与所有模式匹配的行的文件.

--no-index 搜索当前目录中不由Git管理的文件.

-l/ --files-with-matches/ --name-only只显示文件名.

-e下一个参数是模式.默认是使用基本正则表达式.

其他参考考虑:

--threads 要使用的grep工作线程数.

-q/ --quiet/ --silent不输出匹配线; 当匹配时退出状态0.

要改变模式类型,你也可以使用-G/ --basic-regexp(默认),-F/ --fixed-strings,-E/ --extended-regexp,-P/ --perl-regexp,-f file和其他.

  • 这就是SO如此伟大的原因.每个人都有不同的方法来处理问题,并且不时出现意想不到的问题. (3认同)

anu*_*ava 5

gnu-awk脚本可能有效:

cat fileSearch.awk
re == "" {
   exit
}
{
   split($0, null, "\\<(" re "\\>)", b)
   for (i=1; i<=length(b); i++)
      gsub("\\<" b[i] "([|]|$)", "", re)
}
END {
   exit (re != "")
}
Run Code Online (Sandbox Code Playgroud)

然后将其用作:

if awk -v re='string1|string2|string3' -f fileSearch.awk file; then
   echo "all strings were found"
else
   echo "all strings were not found"
fi
Run Code Online (Sandbox Code Playgroud)

或者,您可以将此gnu grep解决方案与PCRE选项一起使用:

grep -qzP '(?s)(?=.*\bstring1\b)(?=.*\bstring2\b)(?=.*\bstring3\b)' file
Run Code Online (Sandbox Code Playgroud)
  • 使用-z我们将grep读取完整的文件转换为单个字符串。
  • 我们使用多个先行断言来断言所有字符串都存在于文件中。
  • 正则表达式必须使用(?s)DOTALLmod 进行跨行.*匹配。

根据man grep

-z, --null-data
   Treat  input  and  output  data as sequences of lines, each terminated by a 
   zero byte (the ASCII NUL character) instead of a newline.
Run Code Online (Sandbox Code Playgroud)

  • 无论这些字符串在文件中的出现顺序如何,任何 `grep` 解决方案都不起作用。前瞻断言只是确保这些字符串存在于文件中的任何位置。所以`grep`也可以写成:`grep -qzP '(?s)(?=.*\bstring3\b)(?=.*\bstring1\b)(?=.*\bstring2\b)'文件` (2认同)

归档时间:

查看次数:

2961 次

最近记录:

6 年,11 月 前