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

我有一个文件 f1:

line1
line2
line3
line4
..
..

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

line2
line8
..
..

我用 catsed做了一些尝试,结果和我想要的完全不一样。我该怎么做呢?

86461 次浏览

grep -v -x -f f2 f1应该可以。

说明:

  • 选择不匹配的行
  • -x仅匹配整行
  • f2中提取模式

我们可以使用 grep -Ffgrep来匹配 f2中的 固定的弦,而不是 模式(如果您想以“如果您得到了什么,您就会看到什么”的方式删除这些行,而不是将 f2中的行作为 regex 模式处理)。

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

comm -2 -3 f1 f2

如果你有 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用于显示生成的两个文件之间没有差异。

这似乎是一个适合 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

对于不太大的排除文件,可以使用 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)(排除-these.txt 大小) + O (n)(来自-this.txt 大小)

其他答案之间的一些时间比较:

$ 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 和 here 字符串一起使用:

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

与 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 快得多,我也更喜欢保持原来的顺序。

你试过用血氧饱和度 这个吗?

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

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

显然对于大文件来说不起作用,但是对我来说确实起到了作用:

  • 我和这个网站没有任何关系(如果你还是不相信我,那么你可以在网上搜索另一个工具; 我用的搜索词是“在线设置差异列表”)
  • 链接的网站似乎在每次列表比较时都会进行网络调用,所以不要向它提供任何敏感数据

使用另一个列表过滤一个列表的 Python 方法。

加载文件:

>>> f1 = open('f1').readlines()
>>> f2 = open('f2.txt').readlines()

删除每行末尾的 n 字符串:

>>> f1 = [i.replace('\n', '') for i in f1]
>>> f2 = [i.replace('\n', '') for i in f2]

只打印 f2文件中的 f1行:

>>> [a for a in f1 if all(b not in a for b in f2)]
$ cat values.txt
apple
banana
car
taxi


$ cat source.txt
fruits
mango
king
queen
number
23
43
sentence is long
so what
...
...

我做了一个小的 shell 脚本来“剔除”在 values.txt 文件中的源文件中的值。

$cat weed_out.sh
from=$1
cp -p $from $from.final
for x in `cat values.txt`;
do
grep -v $x $from.final > $from.final.tmp
mv $from.final.tmp $from.final
done

处决。

$ ./weed_out source.txt

你得到一个很好的清理文件... 。