我有两个文件list.txt
和purchaselist.txt
,它们很大,正在尝试获取最新的购买详细信息(购买清单中有重复项)。
让我们说以下是文件内容:
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 ....
上面的输出是通过从我使用过purchaselist.txt
的{{1}}文件中选择最新行而得出的。 tac
中的值在list.txt
中显示为第18列。这里的问题是文件很大。 purchaselist.txt
包含580k条记录,并在具有约170万条记录的list.txt
中查找这些记录。上面的脚本已经运行了将近20个小时,还没有达到一半。如何在这里优化处理时间?
答案 0 :(得分:2)
该脚本很慢,因为对于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.txt
和purchaselist.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
过滤掉不需要的物品答案 1 :(得分:2)
$ tac purchaselist.txt | awk 'NR==FNR{a[$1]; next} $2 in a{print; delete a[$2]}' list.txt - | tac
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 ....
如果这是真实数据中的匹配字段号,请将$ 2更改为$ 18。上面的方法适用于未排序的数据,应该没有任何内存问题,因为它只是将list.txt中的580k小键字符串存储在awk命令的内存中。
答案 2 :(得分:1)
以下内容要求文件在要加入的列上进行排序。对示例进行了排序,因此假设真实文件可以排序或已经排序是合理的。
join -j 1 list.txt purchaselist.txt | tac | rev | uniq -f 1 | rev | tac
我不知道这样做是否会更好,但至少不包含两个级别的嵌套循环。将测试输入修改为在4444
文件中包含list.txt
后,它将正确产生所需的输出。
1111 300.00
2222 200.00
3333 110.00
4444 800.00