给定一个单词列表,对文本正文进行全字匹配

时间:2015-05-26 22:56:44

标签: regex string bash shell grep

注意:

在开始创业之前,我想指出一些其他SO帖子并没有完全回答我的问题而且与这个问题不重复:

背景

我在一个名为words.txt的文件中有一个单词列表(每行一个单词)。我想从名为file.txt的另一个更大的文件中找到所有行,其中包含来自words.txt的任何单词。但是,我只想要全字匹配。这意味着当来自file.txt的一行包含至少一个找到words.txt的单词的实例&#34时,应该进行匹配;所有这些都是单独的" (我知道这很模糊,所以请允许我解释一下)。

换句话说,应该在以下情况下进行匹配:

  1. 这个词本身就是一行
  2. 这个词被非字母数字/非连字符包围
  3. 单词位于一行的开头,后跟非字母数字/非连字符
  4. 单词位于一行的末尾,前面是非字母数字/非连字符
  5. 例如,如果words.txt中的某个字词为cat,我希望其行为如下:

    cat              #=> match
    cat cat cat      #=> match
    the cat is gray  #=> match
    mouse,cat,dog    #=> match
    caterpillar cat  #=> match
    caterpillar      #=> no match
    concatenate      #=> no match
    bobcat           #=> no match
    catcat           #=> no match
    cat100           #=> no match
    cat-in-law       #=> no match
    

    之前的研究:

    grep命令几乎符合我的需要。它如下:

    grep -wf words.txt file.txt

    选项包括:

    -w, --word-regexp
           Select only those lines containing matches that form whole words.
           The test is that the matching substring must either be at the beginning
           of the line, or preceded by a non-word constituent character.
           Similarly, it must be either at the end of the line or followed by a
           non-word constituent character. Word-constituent characters are
           letters, digits, and the underscore.
    -f FILE, --file=FILE
           Obtain patterns from FILE, one per line. The empty file contains
           zero patterns, and therefore matches nothing.
    

    我遇到的一个大问题是,它将连字符(即-)视为"非单词构成字符"。因此(基于上面的示例)对cat执行全字搜索将返回cat-in-law,这不是我想要的。

    我意识到-w选项可能会为许多人带来预期的效果。但是,在我的特定情况下,如果一个单词(例如cat)后跟/前面有一个连字符,那么我需要将其视为一个较大单词的一部分(例如{{1} })而不是单词本身的实例。

    此外,我知道我可以改变cat-in-law包含正则表达式而不是固定字符串然后使用:

    words.txt

    ,其中

    grep -Ef words.txt file.txt

    但是,我希望避免更改-E, --extended-regexp Interpret PATTERN as an extended regular expression 并使其免于正则表达式模式。

    问题:

    是否有一个简单的bash命令可以让我给它一个单词列表并在文本正文上执行全字匹配?

1 个答案:

答案 0 :(得分:4)

我终于想出了一个解决方案:

grep -Ef <(awk '{print "([^a-zA-Z0-9-]|^)"$0"([^a-zA-Z0-9-]|$)"}' words.txt) file.txt

<强>解释

  • words.txt是我的单词列表(每行一个)。
  • file.txt是我要搜索的文字正文。
  • awk命令将动态预处理words.txt,将每个单词包装在一个特殊的正则表达式中,以定义其正式的开始和结束(根据我上面的问题中发布的规范)。
  • awk命令被<()包围,因此其输出将用作-f选项的输入。
  • 我正在使用-E选项,因为我现在正在输入正则表达式列表而不是words.txt中的固定字符串。

这里的好处是words.txt可以保持人类可读性,并且不必包含一堆正则表达式模式。