【问题标题】:optimizing this script to match lines of one txt file with another优化此脚本以将一个 txt 文件的行与另一个匹配
【发布时间】:2013-06-12 05:47:50
【问题描述】:

好的,所以我充其量是 bash 脚本的新手,但我昨晚深夜编写了这个非常小的脚本,以获取相当大的文本文件(约 300,000 行)每行的前 40 个字符并搜索更大的匹配的文本文件(约 220 万行),然后将所有结果输出到匹配行到一个新的文本文件中。

所以脚本看起来像这样:

#!/bin/bash
while read -r line
  do
  match=${line:0:40}
  grep "$match" large_list.txt
done <"small_list.txt"

然后像这样调用脚本

$ bash my_script.sh > outputfile.txt &

这给了我两个列表之间的所有共同元素。现在这一切都很好,并且慢慢起作用。但是我在 m1.smalll ec2 实例上运行它并且足够公平(对此的处理很糟糕,我可以启动一个更大的实例来处理所有这些,或者在我的桌面上执行并上传文件)。但是,我宁愿学习一种更有效的方式来完成相同的任务,但是我似乎不太明白这一点。任何关于如何最好地完成此任务或更有效地完成任务的花絮将非常非常感谢

为了让您了解它的运行速度有多慢,我在大约 10 小时前启动了脚本,我大约完成了所有比赛的 10%。

另外我不打算使用 bash,所以其他语言的脚本是公平的游戏。我认为专业人士在 S.O.可以轻松改善我的岩石以进行锤击

编辑:添加输入和输出以及有关数据的更多信息

 input: (small text file)
  8E636C0B21E42A3FC6AA3C412B31E3C61D8DD062|Vice S01E09 HDTV XviD-FUM[ettv]|Video TV|http://bitsnoop.com/vice-s01e09-hdtv-xvid-fum-ettv-q49614889.html|http://torrage.com/torrent/36A02E282D49EB7D94ACB798654829493CA929CB.torrent
  3B9403AD73124A84AAE12E83A2DE446149516AC3|Sons of Guns S04E08 HDTV XviD-FUM[ettv]|Video TV|http://bitsnoop.com/sons-of-guns-s04e08-hdtv-xvid-fum-e-q49613491.html|http://torrage.com/torrent/3B9403AD73124A84AAE12E83A2DE446149516AC3.torrent
  C4ADF747050D1CF64E9A626CA2563A0B8BD856E7|Save Me S01E06 HDTV XviD-FUM[ettv]|Video TV|http://bitsnoop.com/save-me-s01e06-hdtv-xvid-fum-ettv-q49515711.html|http://torrage.com/torrent/C4ADF747050D1CF64E9A626CA2563A0B8BD856E7.torrent
  B71EFF95502E086F4235882F748FB5F2131F11CE|Da Vincis Demons S01E08 HDTV x264-EVOLVE|Video TV|http://bitsnoop.com/da-vincis-demons-s01e08-hdtv-x264-e-q49515709.html|http://torrage.com/torrent/B71EFF95502E086F4235882F748FB5F2131F11CE.torrent

 match against (large text file)

  86931940E7F7F9C1A9774EA2EA41AE59412F223B|0|0
  8E636C0B21E42A3FC6AA3C412B31E3C61D8DD062|4|2|20705|9550|21419
  ADFA5DD6F0923AE641F97A96D50D6736F81951B1|0|0
  CF2349B5FC486E7E8F48591EC3D5F1B47B4E7567|1|0|429|428|22248
  290DF9A8B6EC65EEE4EC4D2B029ACAEF46D40C1F|1|0|523|446|14276
  C92DEBB9B290F0BB0AA291114C98D3FF310CF0C3|0|0|21448


 Output: 
  8E636C0B21E42A3FC6AA3C412B31E3C61D8DD062|4|2|20705|9550|21419

附加说明:所以基本上有一个散列,它是输入文件的前 40 个字符(我已经将一个文件的大小减小到原始文件的 15% 左右,所以对于这个文件中的每一行,较大的部分都有一个散列带有一些相应信息的文本文件(我正在匹配)现在它是我想写入新文件的较大文件中的行,因此最后我在较小文本中的所有内容的比例为 1:1文件到我的 output_file.txt 在这种情况下,我显示输入的第一行被匹配(较大文件的第 2 行),然后写入输出文件

【问题讨论】:

  • 请添加一些显示输入和预期输出的示例。就几行……
  • 我现在肯定会这样做
  • 在此处搜索xargs 处理的示例。它可能会有所帮助。祝你好运。
  • 输出不存在小或大:-)
  • 不,对不起,不是,但我只是在选择数据的形式,所以你可以明白......我现在就让它全部匹配

标签: bash optimization scripting large-files


【解决方案1】:

采用this answerawk的解决方案:

awk -F"|" 'NR==FNR{a[$1]=$2;next}{if (a[$1]) print}' small.txt large.txt

【讨论】:

    【解决方案2】:

    一些蟒蛇来救援。

    我使用以下 sn-p 创建了两个文本文件:

    #!/usr/bin/env python
    
    import random
    import string
    
    N=2000000
    for i in range(N):
        s = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(40))
        print s + '|4|2|20705|9550|21419'
    

    一个 300k 和一个 2M 行这给了我以下文件:

    $ ll
    -rwxr-xr-x 1  210 Jun 11 22:29 gen_random_string.py*
    -rw-rw-r-- 1 119M Jun 11 22:31 large.txt
    -rw-rw-r-- 1  18M Jun 11 22:29 small.txt
    

    然后我在 small.txt 的末尾附加了一行到 large.txt 的末尾,这样我就有了匹配的模式

    然后是更多的python:

    #!/usr/bin/env python
    
    target = {}
    
    with open("large.txt") as fd:
        for line in fd:
            target[line.split('|')[0]] = line.strip()
    
    with open("small.txt") as fd:
        for line in fd:
            if line.split('|')[0] in target:
                print target[line.split('|')[0]]
    

    一些时间安排:

    $ time ./comp.py 
    3A8DW2UUJO3FYTE8C5ESE25IC9GWAEJLJS2N9CBL|4|2|20705|9550|21419
    
    real    0m2.574s
    user    0m2.400s
    sys 0m0.168s
    
    $ time awk -F"|" 'NR==FNR{a[$1]=$2;next}{if (a[$1]) print}' small.txt large.txt
    3A8DW2UUJO3FYTE8C5ESE25IC9GWAEJLJS2N9CBL|4|2|20705|9550|21419
    
    real    0m4.380s
    user    0m4.248s
    sys 0m0.124s
    

    更新:

    为了节省内存,请以另一种方式进行字典查找

    #!/usr/bin/env python
    
    target = {}
    
    with open("small.txt") as fd:
        for line in fd:
            target[line.split('|')[0]] = line.strip()
    
    with open("large.txt") as fd:
        for line in fd:
            if line.split('|')[0] in target:
                print line.strip()
    

    【讨论】:

    • @FredrikPhil 非常感谢...我排除 Ansgar Wiechers 的回答只是因为在我的 ec2 实例上运行你的 python 脚本时出现内存错误(内存不足)我在我的桌面虽然它确实有效!因此,当我在接近启动应用程序时扩展到更大的实例时,我会将它放在我的后袋中。再次感谢! +1
    • 很高兴我能帮上忙。这是一项有趣的任务。通常您会使用mywiki.wooledge.org/BashFAQ/036 中概述的方法,但这种方法不同。
    • @brendanmorrison - BTW 更新了 python 代码以将所需的内存减少近七倍:-)
    猜你喜欢
    • 2013-08-29
    • 2019-11-19
    • 1970-01-01
    • 2020-09-14
    • 2011-08-15
    • 1970-01-01
    • 2021-09-11
    • 1970-01-01
    • 2021-09-30
    相关资源
    最近更新 更多