行の順序を維持しながら重複行を削除する

行の順序を維持しながら重複行を削除する
[root@server]# awk '!seen[$0]++' out.txt > cleaned
awk: (FILENAME=out.txt FNR=8547098) fatal error: internal error
Aborted
[root@server]#

「サーバー」には8GB RAM + 16GB SWAP、x> 300GBの空き容量、amd64、デスクトップCPUがあります。サイエンティフィックLinux 6.6。その上にロードする他の実行項目はありません。数秒後に awk が停止します。 out.txtは約1.6GBです。GNUを目覚めさせる3.1.7。

質問:行の順序を維持しながら重複行を削除する方法は?大文字と小文字も重要です。たとえば、「A」と「a」は2つの異なる行なので、維持する必要があります。ただし、「a」と「a」は重複しているため、最初の項目だけが必要です。

答えは何でもよい。 awkがこれに適していない場合.. perl / sed ..何が問題になるのですか?

[root@server]# ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 61945
max locked memory       (kbytes, -l) 99999999
max memory size         (kbytes, -m) unlimited
open files                      (-n) 999999
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 99999999
cpu time               (seconds, -t) unlimited
max user processes              (-u) 61945
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
[root@server]# 

更新:RHELシステムでこれを試しましたが、中断されませんでしたが、完了するまで待つ時間がありません。 SL LinuxがRHELと異なるのはなぜですか?

アップデート:Ubuntu 14仮想マシンを試しています。これまでは動作します!これはulimitの問題ではありません。モック 1.3.3

root@asdf-VirtualBox:~# ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 51331
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 51331
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
root@asdf-VirtualBox:~# 

答え1

これが違いをもたらすかどうかは疑問ですが、Perlで同じことをする方法は次のとおりです。

perl -ne 'print if ++$k{$_}==1' out.txt

問題がメモリ内に一意の行を保持することである場合、awk試みたのと同じ問題が発生します。したがって、別のアプローチは次のとおりです。

cat -n out.txt | sort -k2 -k1n  | uniq -f1 | sort -nk1,1 | cut -f2-

仕組み:

  1. GNUシステムでは、cat -n各行の前に行番号が続き、その後にスペースが続きます。<タブ>特徴。catこの入力表現をsort

  2. sortオプションは、-k2ソート時に2番目のフィールドから行末までの文字のみを考慮し、デフォルトでスペースにsortフィールドを分割するように指示します。(またはcatスペースを挿入して<タブ>)
    続くと、-k1n2sort番目のフィールドが最初に考慮され、次に(-k2同じフィールドの場合)最初のフィールドが考慮されますが、数字でソートされます。したがって、重複した行は表示される順序で並べ替えられます。

  3. 結果は次にパイプされますuniq。最初のフィールドを無視するように求められます。-f1- スペースで区切ることもできます)- 元のファイルの一意の行のリストを生成し、それを再度sort
  4. 今回はsort最初のフィールドをソートします。(cat行番号の挿入)数値的にソート順序を元のファイルの順序に復元し、結果をcut
  5. 最後にcut挿入された行番号を削除しますcat。これは、cut2番目のフィールドから行末までのみ印刷することで可能です。cutデフォルトの区切り記号は次のとおりです。<タブ>特徴)

表示するには:

$ cat file
bb
aa
bb
dd
cc
dd
aa
bb
cc
$ cat -n file | sort -k2 | uniq -f1 | sort -k1 | cut -f2-
bb
aa    
dd
cc

答え2

#!/usr/bin/perl 
use DB_File;
tie %h, 'DB_File';

while(<>){ not $h{$_} and print and $h{$_}=1 }

編集1:本当に効果がありますか? (比較する)

Sol1 : Terdon et all Schwartzian-transform-like one-liner
    cat -n _1 | sort -uk2 | sort -nk1 | cut -f2-

Sol2 : perl  + DB_File (this answer)
    perl dbfile-uniq _1

Sol3 : PO (John W. Gill solution has a similar behavior)
    awk '!seen[$0]++' _1

Sol4: Terdon perl
    perl -ne 'print if ++$k{$_}==1' _1

ケース1:100_000_000個の乱数(それぞれ5ビット)、566Mbytes、31_212個の異なる値:

$ while true ; do echo $RANDOM; done | head -100000000 > _1

ケース2:50_000_000個の乱数(それぞれ10ビット)、516Mbytes、48_351_464個の異なる値:

$ shuf _1 |  sed 'N;s/\n/ /' > _11

(次の数字は非常に正確ではありません):

┌────────┬────────┬────────────────┬────────┬──────┐
│        │ Sol1   │ Sol2           │ Sol3   │ Sol4 │
│        │ sort...│ perl DB        │ awk    │ perl │
├────────┼────────┼────────────────┼────────┼──────┤
│ case 1 │ 6m15   │ 6m17           │ 0m28   │ 0m28 │
├────────┼────────┼────────────────┼────────┴──────┤
│ case 2 │ 11m15  │ 81m44          │ out of memory │
├────────┼────────┼────────────────┼────────┬──────┤
│ case 2 │        │ 5m54 /cache=2G │        │      │
└────────┴────────┴────────────────┴────────┴──────┘

キャッシュを持つsol2は次のとおりです。

use DB_File;
use Fcntl ;

$DB_HASH->{'cachesize'} = 2000_000_000;
tie %h, 'DB_File', "_my.db", O_RDWR|O_CREAT|O_TRUNC, 0640, $DB_HASH;

while(<>){ not $h{$_} and print and $h{$_}=1 }

キャッシュサイズオプションを追加してソートを最適化することもできます(まだ完了していません)。

簡単な結論:

  • sortこれは素晴らしいコマンドです!

答え3

使った

awk -v BINMODE=rw '!($0 in a){a[$0];print}' infile >> outfile

BINMODE=rw : 行末終端を幸せに保ちます。 (私は混合OS環境に住んでいます)

ロジックは簡単です。

現在行が連想配列にない場合は、連想配列に追加され、出力に印刷されます。

この方法にはメモリ制限があります。非常に大きなファイルとファイルセットの場合、制限を克服するためにファイルストアを使用するバリアントを使用しました。

答え4

別の回答として公開する価値があるもう1つのアプローチは、一時ファイルを生成するための分割ファイル方法を使用する代わりに、uniqifierソフトウェア自体内でバッチ処理を実行することです。たとえば、Ruby固有の識別子の実装を使用して、次のように説明します。

require 'set'
line_batch_count = 50000 # tunable parameter
lines_seen = Set.new
line_number = 0
ARGF.each do |line|
   line_number += 1
   if (line_number % line_batch_count) == 0
     lines_seen.clear
   end
   unless lines_seen.include? line
      puts line
      lines_seen << line
   end
end

ハッシュセットを頻繁に消去するのがアイデアです。これにより、これは反復的になります。

$ cat uniqm-input.txt | ruby uniqm-capped.rb | wc -l
   20021

$ cat uniqm-input.txt | ruby uniqm-capped.rb | ruby uniqm-capped.rb | wc -l
    1001

$ cat uniqm-input.txt | ruby uniqm-capped.rb | ruby uniqm-capped.rb | head
1506
1054
1623
1002
1173
1400
1226
1340
1824
1091

したがって、ある反復から次の反復まで行数が変更されないまで、この制限されたバージョンを繰り返し実行できます。

この capped-uniqm 技術は言語に依存しません。lines_seenawk、python、perl、C ++などを使用しても、N行ごとに配列を消去できます。これらの言語はすべてset-clearメソッドを持っています。私はawkこれがdelete標準ではありませんが、共通していると思います。

関連情報