web-dev-qa-db-ja.com

別のファイルにある1つのファイルから行を削除する

ファイルがありますf1

line1
line2
line3
line4
..
..

別のファイルにあるすべての行を削除したいf2

line2
line8
..
..

catsedを使って試してみましたが、これは私が意図したものに近くさえありませんでした。これどうやってするの?

114
lalli

grep -v -x -f f2 f1はトリックを行う必要があります。

説明:

  • -vは一致しない行を選択します
  • -xは行全体のみに一致します
  • -f f2からパターンを取得するにはf2

代わりにgrep -Fまたはfgrepを使用して、パターンではなくf2から固定文字列を使用できます(削除したい場合f2内の行を正規表現パターンとして扱うのではなく、「何が得られたら何を見るか」という方法で行を作成します)。

137
gabuzo

代わりにcommを試してください(f1とf2が「ソート済み」であると仮定)

comm -2 -3 f1 f2

大きすぎないファイルを除外するには、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)(exclude-these.txtサイズ)+ O(n)(from-this.txtサイズ)

12

デニスウィリアムソンの答えと似ています(大部分は構文の変更、たとえば_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は約2,500万、mは約14000です。 awkソリューションはソートよりもはるかに高速であり、元の順序を維持することも好みました。

10
jcsahnwaldt

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
kurumi

他のさまざまな回答のタイミング比較:

$ 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
4
Lri

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
2
Benoit

thisを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
1
Ruan