给出了这个问题Appending a line to a file only if it does not already exist
有没有比@drAlberT提供的解决方案更快的方法?
grep -q -F 'string' foo.bar || echo 'string' >> foo.bar
我已经实现了上述解决方案,我必须在500k行文件上进行迭代(即检查一行是否已经在500k行中设置)。而且,我要运行这个过程很多次,可能是1000到5000万次。不用说它有点慢,因为在我的服务器上运行需要25-30ms(总共3-10天的运行时间)。
编辑:流程如下:我有一个500k行的文件,每次运行时,我可能会得到10-30个新行,并检查它们是否已经存在。如果不是我添加它们,那么我重复多次。我的500k行文件的顺序非常重要,因为我正在使用另一个进程。
EDIT2 :500k行文件总是包含唯一的行,我只关心“满行”,没有子串。
非常感谢!
答案 0 :(得分:3)
将数百万次传递文件转换为包含数百万次操作的脚本将为您节省大量开销。在文件的每次传递中搜索单个标签是非常低效的;你可以搜索尽可能多的标签,只需一次通过文件即可轻松适应内存。
可能还有以下几点。
awk 'NR==FNR { a[$0]++; next }
$0 in a { delete a[$0] }
1
END { for (k in a) print k }' strings bigfile >bigfile.new
如果你不能同时将strings
放入内存中,那么将它分成合适的块显然可以让你在你拥有块的过程中完成这个。
另一方面,如果您已经(有效地)将输入组划分为10-30个标签组,那么显然您只能在一次通过中搜索那些10-30。不过,这应该可以为你提供10-30倍的速度提升。
这假定“线”始终是实线。如果标签可以是输入文件中一行的子字符串,反之亦然,则需要进行一些重构。
答案 1 :(得分:3)
很少有人建议改进:
awk
代替grep
,以便您可以检测字符串并将其写入一个操作中; grep
,请不要使用Bash循环将每个可能的匹配项提供给grep,然后将该单词附加到该文件中。相反,将所有潜在的行读入grep作为匹配(使用-f file_name
)并打印匹配。然后反转匹配并追加倒置匹配。请参阅此处的最后一个管道; 考虑这个awk要添加一行:
$ awk -v line=line_to_append 'FNR==NR && line==$0{f=1; exit}
END{if (!f) print line >> FILENAME}' file
或多行:
$ awk 'FNR==NR {lines[$0]; next}
$0 in lines{delete lines[$0]}
END{for (e in lines) print e >> FILENAME}' lines file
使用Unix words
文件副本(235,886行)和五行lines
文件的一些时间有两个重叠:
$ echo "frob
knob
kabbob
stew
big slob" > lines
$ time awk 'FNR==NR {lines[$0]; next}
$0 in lines{delete lines[$0]}
END{for (e in lines) print e >> FILENAME}' lines words
real 0m0.056s
user 0m0.051s
sys 0m0.003s
$ tail words
zythum
Zyzomys
Zyzzogeton
frob
kabbob
big slob
编辑2
尝试这两者中最好的:
$ time grep -x -f lines words |
awk 'FNR==NR{a[$0]; next} !($0 in a)' - lines >> words
real 0m0.012s
user 0m0.010s
sys 0m0.003s
说明:
grep -x -f lines words
找到字数为awk 'FNR==NR{a[$0]; next} !($0 in a)' - lines
将这些内容反转为不在单词中的行>> words
将这些附加到文件答案 2 :(得分:1)
如果重复项在文件中无效,只需将它们全部附加并过滤掉重复项:
cat myfile mynewlines | awk '!n[$0]++' > mynewfile
这将允许在几秒钟内追加数百万行。
如果订单另外无关紧要且您的文件超过几千兆字节,则可以使用sort -u
代替。
答案 3 :(得分:0)
让脚本在使用原始文件后从stdin读取新行。所有行都存储在一个关联数组中(没有任何压缩,如md5sum)。
附加后缀' x'旨在处理输入,例如' -e&#39 ;;可能存在更好的方式。
#!/bin/bash
declare -A aa
while read line; do aa["x$line"]=1;
done < file.txt
while read line; do
if [ x${aa[$line]} == x ]; then
aa[$line]=1;
echo "x$line" >> file.txt
fi
done