如何通过检查第二个文本文件( main.txt
)从文本文件( main.txt
)中删除所有行。 如果文件大于10-100MB,什么是有效的方法 。 [使用mac]
main.txt 3 1 2 5
删除这些行
removethese.txt 3 2 9
输出:
output.txt 1 5
示例行(这些是我正在使用的实际行 – 订单无关紧要):
ChIJW3p7Xz8YyIkRBD_TjKGJRS0 ChIJ08x-0kMayIkR5CcrF-xT6ZA ChIJIxbjOykFyIkRzugZZ6tio1U ChIJiaF4aOoEyIkR2c9WYapWDxM ChIJ39HoPKDix4kRcfdIrxIVrqs ChIJk5nEV8cHyIkRIhmxieR5ak8 ChIJs9INbrcfyIkRf0zLkA1NJEg ChIJRycysg0cyIkRArqaCTwZ-E8 ChIJC8haxlUDyIkRfSfJOqwe698 ChIJxRVp80zpcEARAVmzvlCwA24 ChIJw8_LAaEEyIkR68nb8cpalSU ChIJs35yqObit4kR05F4CXSHd_8 ChIJoRmgSdwGyIkRvLbhOE7xAHQ ChIJaTtWBAWyVogRcpPDYK42-Nc ChIJTUjGAqunVogR90Kc8hriW8c ChIJN7P2NF8eVIgRwXdZeCjL5EQ ChIJizGc0lsbVIgRDlIs85M5dBs ChIJc8h6ZqccVIgR7u5aefJxjjc ChIJ6YMOvOeYVogRjjCMCL6oQco ChIJ54HcCsaeVogRIy9___RGZ6o ChIJif92qn2YVogR87n0-9R5tLA ChIJ0T5e1YaYVogRifrl7S_oeM8 ChIJwWGce4eYVogRcrfC5pvzNd4
有两种标准的方法来做到这一点:
用grep
:
grep -vxFf removethese main
这使用:
-v
反转比赛。 -x
匹配整条线,以防止he
匹配hello
或highway to hell
等highway to hell
。 -F
使用固定字符串,以便参数按原样被使用,而不是被解释为正则表达式。 -f
从另一个文件中获取模式。 在这种情况下,从removethese
。 用awk
:
$ awk 'FNR==NR {a[$0];next} !($0 in a)' removethese main 1 5
像这样,我们将每一行存储在一个数组a[]
。 然后,我们读取main
文件,只是打印数组中不存在的那些行。
用grep
:
grep -vxFf removethese.txt main.txt >output.txt
用fgrep
:
fgrep -vxf removethese.txt main.txt >output.txt
fgrep
已被弃用。 fgrep --help
说:
作为“fgrep”的调用已被弃用; 用“grep -F”代替。
用awk
(来自@fedorqui):
awk 'FNR==NR {a[$0];next} !($0 in a)' removethese.txt main.txt >output.txt
用sed
:
sed "s=^=/^=;s=$=$/d=" removethese.txt | sed -f- main.txt >output.txt
如果removethese.txt包含特殊字符,这将失败。 为此你可以这样做:
sed 's/[^^]/[&]/g; s/\^/\\^/g' removethese.txt >newremovethese.txt
并在sed
命令中使用这个newremovethese.txt 。 但这样做不值得,与其他方法相比,速度太慢了。
对上述方法进行测试:
sed
方法花费太多时间,不值得测试。
使用的文件:
removethese.txt : Size: 15191908 (15MB) Blocks: 29672 Lines: 100233 main.txt : Size: 27640864 (27.6MB) Blocks: 53992 Lines: 180034
命令:
grep -vxFf
| fgrep -vxf
| awk
花费时间:
0m7.966s
| 0m7.823s
| 0m0.237s
0m7.877s
| 0m7.889s
| 0m0.241s
0m7.971s
| 0m7.844s
| 0m0.234s
0m7.864s
| 0m7.840s
| 0m0.251s
0m7.798s
| 0m7.672s
| 0m0.238s
0m7.793s
| 0m8.013s
| 0m0.241s
AVG
0m7.8782s
| 0m7.8468s
| 0m0.2403s
这个测试结果意味着fgrep
比grep
快一点。
awk
方法(从@fedorqui)通过测试飞行颜色( 0.2403 seconds
只!!!)。
测试环境:
HP ProBook 440 G1 Laptop 8GB RAM 2.5GHz processor with turbo boost upto 3.1GHz RAM being used: 2.1GB Swap being used: 588MB RAM being used when the grep/fgrep command is run: 3.5GB RAM being used when the awk command is run: 2.2GB or less Swap being used when the commands are run: 588MB (No change)
测试结果:
使用awk
方法。
我喜欢@ fedorqui使用awk进行设置,其中一个具有足够的内存以适应所有的“删除这些”行:一个内存中方法的简洁表达式。
但是,对于要移除的行的大小相对于当前内存较大的情况,将这些数据读入内存中的数据结构是一个失败或异常的邀请,请考虑一种古老的方法:sort / join
sort main.txt > main_sorted.txt sort removethese.txt > removethese_sorted.txt join -t '' -v 1 main_sorted.txt removethese_sorted.txt > output.txt
笔记:
以下是我找到的很多简单有效的解决方案: http : //www.catonmat.net/blog/set-operations-in-unix-shell-simplified/
您需要使用Set Complement
bash命令之一。 100MB文件可以在几秒钟或几分钟内解决。
设置会员
$ grep -xc 'element' set # outputs 1 if element is in set # outputs >1 if set is a multi-set # outputs 0 if element is not in set $ grep -xq 'element' set # returns 0 (true) if element is in set # returns 1 (false) if element is not in set $ awk '$0 == "element" { s=1; exit } END { exit !s }' set # returns 0 if element is in set, 1 otherwise. $ awk -ve='element' '$0 == e { s=1; exit } END { exit !s }'
设置平等
$ diff -q <(sort set1) <(sort set2) # returns 0 if set1 is equal to set2 # returns 1 if set1 != set2 $ diff -q <(sort set1 | uniq) <(sort set2 | uniq) # collapses multi-sets into sets and does the same as previous $ awk '{ if (!($0 in a)) c++; a[$0] } END{ exit !(c==NR/2) }' set1 set2 # returns 0 if set1 == set2 # returns 1 if set1 != set2 $ awk '{ a[$0] } END{ exit !(length(a)==NR/2) }' set1 set2 # same as previous, requires >= gnu awk 3.1.5
设置基数
$ wc -l set | cut -d' ' -f1 # outputs number of elements in set $ wc -l < set $ awk 'END { print NR }' set
子集测试
$ comm -23 <(sort subset | uniq) <(sort set | uniq) | head -1 # outputs something if subset is not a subset of set # does not putput anything if subset is a subset of set $ awk 'NR==FNR { a[$0]; next } { if !($0 in a) exit 1 }' set subset # returns 0 if subset is a subset of set # returns 1 if subset is not a subset of set
设置联盟
$ cat set1 set2 # outputs union of set1 and set2 # assumes they are disjoint $ awk 1 set1 set2 # ditto $ cat set1 set2 ... setn # union over n sets $ cat set1 set2 | sort -u # same, but assumes they are not disjoint $ sort set1 set2 | uniq # sort -u set1 set2 $ awk '!a[$0]++' # ditto
设置交叉口
$ comm -12 <(sort set1) <(sort set2) # outputs insersect of set1 and set2 $ grep -xF -f set1 set2 $ sort set1 set2 | uniq -d $ join <(sort -n A) <(sort -n B) $ awk 'NR==FNR { a[$0]; next } $0 in a' set1 set2
设置补充
$ comm -23 <(sort set1) <(sort set2) # outputs elements in set1 that are not in set2 $ grep -vxF -f set2 set1 # ditto $ sort set2 set2 set1 | uniq -u # ditto $ awk 'NR==FNR { a[$0]; next } !($0 in a)' set2 set1
设置对称差异
$ comm -3 <(sort set1) <(sort set2) | sed 's/\t//g' # outputs elements that are in set1 or in set2 but not both $ comm -3 <(sort set1) <(sort set2) | tr -d '\t' $ sort set1 set2 | uniq -u $ cat <(grep -vxF -f set1 set2) <(grep -vxF -f set2 set1) $ grep -vxF -f set1 set2; grep -vxF -f set2 set1 $ awk 'NR==FNR { a[$0]; next } $0 in a { delete a[$0]; next } 1; END { for (b in a) print b }' set1 set2
电源设置
$ p() { [ $# -eq 0 ] && echo || (shift; p "$@") | while read r ; do echo -e "$1 $r\n$r"; done } $ p `cat set` # no nice awk solution, you are welcome to email me one: # peter@catonmat.net
设置笛卡尔积
$ while read a; do while read b; do echo "$a, $b"; done < set1; done < set2 $ awk 'NR==FNR { a[$0]; next } { for (i in a) print i, $0 }' set1 set2
不相交集合测试
$ comm -12 <(sort set1) <(sort set2) # does not output anything if disjoint $ awk '++seen[$0] == 2 { exit 1 }' set1 set2 # returns 0 if disjoint # returns 1 if not
空集测试
$ wc -l < set # outputs 0 if the set is empty # outputs >0 if the set is not empty $ awk '{ exit 1 }' set # returns 0 if set is empty, 1 otherwise
最低限度
$ head -1 <(sort set) # outputs the minimum element in the set $ awk 'NR == 1 { min = $0 } $0 < min { min = $0 } END { print min }'
最大
$ tail -1 <(sort set) # outputs the maximum element in the set $ awk '$0 > max { max = $0 } END { print max }'