linux - 优化大文件的 grep 操作

标签 linux bash grep do-while

我有两个文件 list.txtpurchaselist.txt,它们相当大,我正在尝试获取最新的购买详细信息(purchaselist 中有重复项)。

假设以下是文件内容:

list.txt

1111
2222
3333

purchaselist.txt

0001 1111 210.00 abcd 10 A 151234 181234 .... 
0011 1111 300.00 abcd 10 A 151000 181222 ....
0022 2222 110.00 abcd 10 E 151111 181000 ....
0099 2222 200.00 abcd 10 A 151222 181999 ....
0033 3333 110.00 abcd 10 A 151000 181222 ....
0044 0044 500.00 abcd 10 A 151999 181333 ....
8899 4444 800.00 abcd 10 A 153333 181777 ....

我使用 grep 和一个简单的 do while 循环来完成此操作。这是我的命令:

while read line; do tac purchaselist.txt | grep -m1 $line; done < list.txt >> result.txt

我的预期输出是,它已经看起来像这样:

0011 1111 300.00 abcd 10 A 151000 181222 ....
0099 2222 200.00 abcd 10 A 151222 181999 ....
0033 3333 110.00 abcd 10 A 151000 181222 ....

上面的输出是通过从我使用 tacpurchaselist.txt 文件中选取最新行得出的。 list.txt 中的值在 purchaselist.txt 中显示为第 18 列。这里的问题是文件很大。 list.txt 包含 580k 条记录,并在包含约 170 万条记录的 purchaselist.txt 中查找这些记录。上面的脚本已经运行了快20个小时了,还没有跑到一半。我如何优化这里的处理时间?

最佳答案

脚本速度很慢,因为对于 list.txt 中的每个单词您已阅读全文purchaselist.txt ,在您的情况下,它将被读取 580K 次。此外,bash 在大型迭代中运行速度并不快。

如果可以接受其他方法,可以使用 datamash :

datamash -t ' ' -g 1 last 2 < purchaselist.txt
  • -t ' '字段分隔符=空格
  • -g 1按字段1分组
  • last 2字段 2 的最后一个值

顺便说一句,4444不在 list.txt 中但显示在最终输出中,所以我假设 list.txt不需要。如果这是一个拼写错误,您可以使用 datamash -t ' ' -g 1 last 2 < purchaselist.txt | grep -f list.txt .

此外,如果 datamash尚未安装,您没有权限安装可以使用的软件包 awk相反:

awk 'ARGIND==1{a[$0]}ARGIND==2{b[$1]=$2}END{for(i in a)if(i in b)print i,b[i]}' list.txt purchaselist.txt

该命令由三部分组成ARGIND == 1 ARGIND == 2 END :

  • ARGIND == 1表示参数索引 1(您可以将其视为 argv[1]list.txt )
  • a[$0] $0表示整行,放入字典中
  • b[$1] = $2创建另一个字典存储每个项目( $2 )的价格( $1 ,第二个字段),现有值以这种方式覆盖
  • END处理完这两个文件后
  • for (i in a) if (i in b)如果两者都在 file.txtpurchaselist.txt
  • print i,b[i]打印键和值

编辑 对于非 GNU awk ,可以使用

awk 'NR==FNR{a[$0];next}{b[$1]=$2}END{for(i in a)if(i in b)print i,b[i]}' list.txt purchaselist.txt

编辑 好的...如果您有多个字段:

tac purchaselist.txt | sort -suk2,2 | grep -f list.txt
  • tac让最新的优先
  • -s稳定排序以保持原始顺序
  • -u采取独特的 -k2,2 (第二个字段)即只保留特定键值的第一条记录
  • -k2,2使用 2 到 2 的字段作为键
  • grep过滤掉不需要的项目

关于linux - 优化大文件的 grep 操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57734888/

相关文章:

运行 start-stop-daemon 的能力

c - 有没有更好的方法来读取/proc 中每个进程目录中的 cmdline 文件

bash - 多个命令的 Unix 别名参数

java - Java使用的内存多于堆大小(或正确大小的Docker内存限制)

linux - 为什么共享库的 ELF header 将 Linux 指定为 OSABI?

regex - 用 sed 替换正则表达式组

bash - 如何根据上一个命令的退出代码更改 bash 提示颜色?

shell - 从多个文件的 grep 搜索中获取最后一行

unicode - grep 二进制文件和 UTF16

regex - 如何grep一个shell变量来匹配行尾?