【问题标题】:How do I grep for words coming from a file in files listed in a file?如何在文件中列出的文件中查找来自文件的单词?
【发布时间】:2011-07-08 14:39:54
【问题描述】:

在单个文件中搜索单词很容易:

grep stuff file.txt

但是我有很多文件,每个是files.txt 中的一行,还有很多我要查找的单词,每个都是words.txt 中的一行。输出应该是一个文件,每行a => bawords.txt 中的行号,bfiles.txt 中的行号。

我需要在 OSX 上运行它,所以最好在 shell 中运行一些简单的东西,但任何其他语言都可以。我自己对 shell 脚本没有太多经验,而且我更习惯于对字符串搜索无用的语言(即 C - 我猜 Perl 或 Python 可能会有所帮助,但我没有使用过它们)。

【问题讨论】:

  • 可以使用 grep -f 提供带有搜索条件的文件并使用 find 获取要搜索的文件列表。以下对我有用: find . -name '*.py' -exec grep -n -f search_terms.txt '{}' \;

标签: python perl macos shell


【解决方案1】:

您可能会这样更快、更 Pythonic 并且更容易理解:

with open("words.txt") as words:
    wlist=[(ln,word.strip()) for ln,word in enumerate(words,1)]

with open("files.txt") as files:
    flist=[(ln,file.strip()) for ln,file in enumerate(files,1)]

for filenum, filename in flist:
    with open(filename) as fdata:
        for fln,line in enumerate(fdata,1):
            for wln, word in wlist:
                if word in line:
                    print "%d => %d" % (wln, fln)

【讨论】:

  • 最后一行应该是 (wln,filenum)
  • @Zeophlite:帮帮我。我按要求切换两者。我错过了什么吗?
  • 只是 fln 应该是 filenum 以符合原始规范 - 没什么大不了的:D
【解决方案2】:

这是一个带有 awk 的两部分: 1.扫描files.txt中的每个文件,并将单词编号映射到文件名 2.将文件名映射到files.txt中的行号

awk '
  NR == FNR {word[$1] = NR; next}
  {for (i=1; i<=NF; i++) {if ($i in word) {print word[$i] " => " FILENAME; break}}}
' words.txt $(<files.txt) | 
sort -u |
awk '
  NR == FNR {filenum[$1] = NR; next}
  {$3 = filenum[$3]; print}
' files.txt -

【讨论】:

    【解决方案3】:

    首先,学会指定感兴趣的文件。在一个目录或多个目录中? Unix find 实用程序将执行此操作。

    在 Bash 提示符下:

    $ cd [the root directory where your files are]
    $ find . -name "*.txt"
    

    您没有说,但假设文件可以用“星点”来描述,然后 find 会找到文件。

    接下来,将文件名通过管道传递给您要对它们执行的操作:

    $ find . -name "*.txt" -print0 | xargs -0 egrep 'stuff'
    

    这将在每个文件上运行egrep,搜索模式为stuff

    谷歌find 加上xargs 有数以千计的例子。一旦您可以轻松找到文件 - 重新表述您的问题,以便更清楚您想要对它们做什么。然后我可以用 Perl 帮助你做到这一点。

    【讨论】:

    • 我冒昧地将一些双引号更改为单引号,以使示例按预期运行。
    • 另一个有用的选项是xargs -d,例如find . -type | fgrep ' ' | xargs -d '\n' ls -l,用于列出名称中包含空格的所有文件。
    • @reinierpost:双引号在此答案的原始版本中的使用方式非常好。无需更改它们。
    【解决方案4】:

    python 中的以下脚本可以做到这一点。这是我第一次尝试 python,所以我会很感激任何 cmets

    flist = open('files.txt')
    
    filenum = 0
    for filename in flist:
        filenum = filenum + 1
        filenamey = filename.strip()
        filedata = open(filenamey)
        for fline in filedata:
            wordnum = 0
            wlist = open('words.txt')
            for word in wlist:
                wordnum = wordnum + 1
                sword = word.strip()
                if sword in fline:
                    s = repr(filenum) + ' => ' + repr(wordnum)
                    print s
    

    【讨论】:

    • 啊,我明白了。我以前不明白你想要什么。我会更新我的答案。
    【解决方案5】:

    这里有一些东西可以做你想做的事,但唯一的事情是它不会打印出匹配的单词,而只是打印出匹配的行、文件名和行号.但是,如果您在 grep 上使用--color=auto,它将使用您在${GREP_COLOR} 中设置的任何内容突出显示匹配的单词,默认为红色。

    cat files.txt | xargs grep -nf words.txt --color=auto
    

    此命令将逐行转储files.txt 的所有内容,并将文件名通过管道传输到grep,grep 将在文件中搜索与words.txt 匹配的每个单词。与files.txt 类似,words.txt 应该是您想要用换行符分隔的所有搜索词。

    如果您的 grep 是使用 perl 正则表达式引擎构建的,那么,如果您将 -P 选项传递给 grep,则可以使用 Perl 正则表达式,如下所示:

    grep -Pnf words.txt --color=auto
    

    希望这会有所帮助。

    更新:起初,我不确定@Zeophlite 在问什么,但在他发布了他的示例之后,我明白了他想要什么。这是他想做的python实现:

    from contextlib import nested
    
    
    def search_file(line_num, filename):
        with nested(open(filename), open('words.txt')) as managers:
            open_filename, word_file = managers
            for line in open_filename:
                for wordfile_line_number, word in enumerate(word_file, 1):
                    if word.strip() in line:
                        print "%s => %s" % (line_num, wordfile_line_number)
    
    
    with open('files.txt') as filenames_file:
        for filenames_line_number, fname in enumerate(filenames_file, 1):
            search_file(filenames_line_number, fname.strip())
    

    【讨论】:

    • 您的while 循环只是重新实现cat
    【解决方案6】:

    在纯shell中做,我很接近:

    $ grep -n $(tr '\n' '|' < words.txt | sed 's/|$//') $(cat files.txt)
    

    (试图弄清楚如何删除$(cat files.txt),但无法)

    这会打印出每个文件中的单词,并打印出它们出现的行,但不会打印出该单词所在的words.txt 中的行。

    我可以做一些非常丑陋(如果您认为这还不够丑陋)的东西,但您真正的答案是使用更高级别的语言。 awk 解决方案是 shellish,因为现在大多数人认为 awk 只是 Unix 环境的一部分。但是,如果您使用的是awk,则不妨使用perlpythonruby

    awk 的唯一优势是它会自动包含在 Linux/Unix 发行版中,即使创建发行版的用户没有包含任何开发包。这种情况很少见,但确实会发生。

    【讨论】:

      【解决方案7】:

      满足您的需求

      .

      您的代码:

      flist = open('files.txt') 
      
      filenum = 0 
      for filename in flist: 
          filenum = filenum + 1 
          filenamey = filename.strip() 
          filedata = open(filenamey) 
          for fline in filedata: 
              wordnum = 0 
              wlist = open('words.txt') 
              for word in wlist: 
                  wordnum = wordnum + 1 
                  sword = word.strip() 
                  if sword in fline: 
                      s = repr(filenum) + ' => ' + repr(wordnum) 
                      print s 
      

      您打开 'files.txt' 但不要关闭它。 with open('files.txt') as flist: 更可取,因为它的文本更清晰,并且可以单独关闭。

      enumerate()代替filenum = filenum + 1
      从现在开始,你一定不要忘记enumerate(),因为它是一个非常有用的功能。它的工作速度也非常非常快。

      fline 对于线的迭代器来说不是一个好名字,IMO; line不是很好吗?

      指令wlist = open('words.txt') 不是一个好地方:它不仅针对每个打开的文件执行,甚至在每次分析一行时执行。 此外,每次wlist迭代时,即在每一行都执行对wlist中列出的名称的处理。您必须将此处理排除在所有迭代之外。

      wordnum就是wlistword的索引。您可以再次使用 enumerate() 或简单地使用索引 i 循环并使用 wlist[i] 而不是 word

      每次wlist在排队,你就这样做

      print repr(filenum) + ' => ' + repr(wordnum) 
      

      最好使用print repr(filenum) + ' =&gt; ' + repr(all_wordnum),其中all_wordnum 是一行中所有的列表

      您将单词列表保存在文件中。你最好序列化这个词的列表。查看模块 picklepickle

      在结果记录方面也有一些需要改进的地方。因为执行指令

      print repr(filenum) + ' => ' + repr(wordnum)
      

      每次都不是一个好习惯。如果你想记录在一个文件中也是一样的:你不能重复命令write()更好的是将所有结果列出在一个列表中,并在处理结束时打印或记录,使"\n".join(list)或类似的东西

      【讨论】:

        【解决方案8】:

        sh 答案,假设单词或文件名不包含任何 shell 元字符,例如空格:

        nw=0; while read w; do nw=`expr $nw + 1`; nf=0; { while read f; do nf=`expr $nf + 1`; fgrep -n $w $f | sed 's/:.*//' | while read n; do echo $nw =\> $nf; done; done < /tmp/files.txt;}; done < /tmp/words.txt
        

        但我更喜欢 Perl 来处理这种事情。 并且 Perl 脚本不会像 carrrot-top 的 Python 代码那样简短或可读,除非你使用 IO::All

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-02-11
          • 2011-12-15
          • 2013-02-02
          • 2012-12-02
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多