我想检查我的所有字符串是否都存在于文本文件中.它们可以存在于同一条线上或不同的线上.部分匹配应该没问题.像这样:
...
string1
...
string2
...
string3
...
string1 string2
...
string1 string2 string3
...
string3 string1 string2
...
string2 string3
... and so on
在上面的例子中,我们可以用正则表达式代替字符串.
例如,以下代码检查文件中是否存在任何字符串:
if grep -EFq "string1|string2|string3" file; then
  # there is at least one match
fi
如何检查是否所有这些都存在?由于我们只对所有匹配项的存在感兴趣,因此我们应该在所有字符串匹配后立即停止读取文件.
是否有可能做到这一点,而不必调用grep多次(不会标尺时输入文件较大,或者如果我们有一个大量的字符串相匹配),或使用工具,如awk或python?
此外,是否有一个字符串的解决方案可以很容易地扩展为正则表达式?
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
这里有一堆其他信息和选项:
假设你真的在寻找字符串,那就是:
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
一旦所有字符串匹配,上述内容将立即停止读取文件.
如果您正在寻找regexps而不是字符串,那么使用GNU awk进行多字符RS并在END部分保留$ 0,您可以:
awk -v RS='^$' 'END{exit !(/regexp1/ && /regexp2/ && /regexp3/)}' file
实际上,即使它是字符串你也可以做:
awk -v RS='^$' 'END{exit !(index($0,"string1") && index($0,"string2") && index($0,"string3"))}' file
上面两个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
对于正则表达式它是:
awk '
NR==FNR { regexps[$0]; next }
{
    for (regexp in regexps)
        if ( $0 !~ regexp )
            exit 1
}
' file_of_regexps RS='^$' file_to_be_searched
如果您没有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
和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
git grep以下是使用git grep多种模式的语法:
git grep --all-match --no-index -l -e string1 -e string2 -e string3 file
您还可以将模式与布尔表达式组合,例如--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和其他.
此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 != "")
}
然后将其用作:
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
或者,您可以将此gnu grep解决方案与PCRE选项一起使用:
grep -qzP '(?s)(?=.*\bstring1\b)(?=.*\bstring2\b)(?=.*\bstring3\b)' file
-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.
| 归档时间: | 
 | 
| 查看次数: | 2961 次 | 
| 最近记录: |