从一个文件中删除另一个文件中的行

时间:2011-01-24 09:03:32

标签: bash scripting sh

我有一个文件f1

line1
line2
line3
line4
..
..

我想删除另一个文件f2中的所有行:

line2
line8
..
..

我尝试使用catsed,但这与我的意图并不相符。我怎么能这样做?

9 个答案:

答案 0 :(得分:131)

grep -v -x -f f2 f1应该可以解决问题。

说明:

  • -v选择不匹配的行
  • -x仅匹配整行
  • -f f2f2
  • 获取模式

可以使用grep -Ffgrep来匹配来自f2固定字符串而不是模式(如果您需要删除“你所看到的是什么”的方式,而不是将f2中的行视为正则表达式模式。)

答案 1 :(得分:48)

尝试使用comm(假设f1和f2已经“已经排序”)

comm -2 -3 f1 f2

答案 2 :(得分:12)

对于不太大的排除文件,可以使用AWK的关联数组。

awk 'NR == FNR { list[tolower($0)]=1; next } { if (! list[tolower($0)]) print }' exclude-these.txt from-this.txt 

输出的顺序与“from-this.txt”文件的顺序相同。如果您需要,tolower()函数会使其不区分大小写。

算法复杂度可能是O(n)(不包括--the.txt大小)+ O(n)(来自-t.txt大小)

答案 3 :(得分:10)

类似于Dennis Williamson的回答(主要是语法上的变化,例如明确设置文件编号而不是NR == FNR技巧):

awk '{if (f==1) { r[$0] } else if (! ($0 in r)) { print $0 } } ' f=1 exclude-these.txt f=2 from-this.txt

访问r[$0]会为该行创建条目,无需设置值。

假设awk使用具有常量查找和(平均)恒定更新时间的哈希表,其时间复杂度将为O(n + m),其中n和m是文件的长度。就我而言,n约为2500万,m~14000。 awk解决方案比sort更快,我也更喜欢保留原始订单。

答案 4 :(得分:5)

如果你有Ruby(1.9 +)

#!/usr/bin/env ruby 
b=File.read("file2").split
open("file1").each do |x|
  x.chomp!
  puts x if !b.include?(x)
end

其中O(N ^ 2)的复杂性。如果你想关心性能,这里是另一个版本

b=File.read("file2").split
a=File.read("file1").split
(a-b).each {|x| puts x}

使用散列来实现减法,因此是复杂度O(n)(a的大小)+ O(n)(b的大小)

这里有一个小基准,由user576875提供,但有100K行,以上:

$ for i in $(seq 1 100000); do echo "$i"; done|sort --random-sort > file1
$ for i in $(seq 1 2 100000); do echo "$i"; done|sort --random-sort > file2
$ time ruby test.rb > ruby.test

real    0m0.639s
user    0m0.554s
sys     0m0.021s

$time sort file1 file2|uniq -u  > sort.test

real    0m2.311s
user    0m1.959s
sys     0m0.040s

$ diff <(sort -n ruby.test) <(sort -n sort.test)
$

diff用于表示生成的2个文件之间没有差异。

答案 5 :(得分:4)

其他各种答案之间的时间比较:

$ for n in {1..10000}; do echo $RANDOM; done > f1
$ for n in {1..10000}; do echo $RANDOM; done > f2
$ time comm -23 <(sort f1) <(sort f2) > /dev/null

real    0m0.019s
user    0m0.023s
sys     0m0.012s
$ time ruby -e 'puts File.readlines("f1") - File.readlines("f2")' > /dev/null

real    0m0.026s
user    0m0.018s
sys     0m0.007s
$ time grep -xvf f2 f1 > /dev/null

real    0m43.197s
user    0m43.155s
sys     0m0.040s

sort f1 f2 | uniq -u甚至不是对称差异,因为它会删除在任一文件中多次出现的行。

comm也可以与stdin和字符串一起使用:

echo $'a\nb' | comm -23 <(sort) <(sort <<< $'c\nb') # a

答案 6 :(得分:2)

似乎是适合SQLite shell的工作:

create table file1(line text);
create index if1 on file1(line ASC);
create table file2(line text);
create index if2 on file2(line ASC);
-- comment: if you have | in your files then specify “ .separator ××any_improbable_string×× ”
.import 'file1.txt' file1
.import 'file2.txt' file2
.output result.txt
select * from file2 where line not in (select line from file1);
.q

答案 7 :(得分:1)

您是否使用sed尝试

sed 's#^#sed -i '"'"'s%#g' f2 > f2.sh

sed -i 's#$#%%g'"'"' f1#g' f2.sh

sed -i '1i#!/bin/bash' f2.sh

sh f2.sh

答案 8 :(得分:0)

这不是一个“编程”答案,但这是一个快速而肮脏的解决方案:只需转到http://www.listdiff.com/compare-2-lists-difference-tool

显然不适用于海量文件,但对我有用。一些注意事项:

  • 我不以任何方式隶属于该网站(如果您仍然不相信我,那么您可以在线搜索其他工具;我使用搜索词“在线设置差异列表”)
  • 链接的网站似乎会在每个列表比较中进行网络通话,因此不要向其提供任何敏感数据