Unixでソートせずにファイルの重複行を削除するにはどうすればよいですか?


136

Unixでファイル内の重複する行を削除する方法はありますか?

私はそれを行うことができますsort -uし、uniqコマンドが、私は使用したいですsedawk。それは可能ですか?


11
連続する重複を意味する場合uniqは、単独で十分です。
マイケルクレリン-ハッカー2009年

さもなければ、それはで可能であると信じていawkますが、大きなファイルではかなりのリソースを消費します。
マイケルクレリン-ハッカー2009年

重複するstackoverflow.com/q/24324350およびstackoverflow.com/q/11532157には興味深い答えがあり、理想的にはここに移行する必要があります。
tripleee 2018年

回答:


290
awk '!seen[$0]++' file.txt

seenAwkがファイルのすべての行を渡す連想配列です。行が配列にない場合、seen[$0]評価はfalseになります。!NOT論理演算子であるとtrueに偽を反転します。Awkは、式がtrueと評価される行を出力します。++増分seenようにseen[$0] == 1最初の時間の後の行は、その後発見されseen[$0] == 2、等々 。
awkは、0and ""(空の文字列)以外をすべてtrueに評価します。重複行が中に配置されている場合seen、その後!seen[$0]はfalseと評価され、行は出力に書き込まれることはありません。


5
我々はこれを行うことができ、それをファイルに保存するにはawk '!seen[$0]++' merge_all.txt > output.txt
AkashさんKandpal

5
ここでの重要な注意点:複数のファイルに対してこれを行う必要があり、コマンドの最後にさらにファイルを追加するか、ワイルドカードを使用する場合、「seen」配列はすべてのファイルからの重複行でいっぱいになります。代わりに、各ファイルを個別に扱いたい場合は、次のようにする必要がありますfor f in *.txt; do gawk -i inplace '!seen[$0]++' "$f"; done
Nick K9

複数のファイルにわたって累積的に重複除外する@ NickK9は、それ自体が素晴らしいです。いいヒント
sfscs

31

http://sed.sourceforge.net/sed1line.txtから:(これがどのように機能するか私に尋ねないでください;-))

 # delete duplicate, consecutive lines from a file (emulates "uniq").
 # First line in a set of duplicate lines is kept, rest are deleted.
 sed '$!N; /^\(.*\)\n\1$/!P; D'

 # delete duplicate, nonconsecutive lines from a file. Beware not to
 # overflow the buffer size of the hold space, or else use GNU sed.
 sed -n 'G; s/\n/&&/; /^\([ -~]*\n\).*\n\1/d; s/\n//; h; P'

geekery ;-) +1、ただしリソースの消費は避けられません。
マイケルクレリン-ハッカー、2009年

3
'$!N; /^(.*)\n\1$/!P; D 'は「最後の行にいない場合は、別の行を読みます。次に、現在の内容を確認し、それがIS N'Tの後に改行が続き、次に同じものがある場合は、その内容を印刷してください。今すぐ削除してください。もの(改行まで)。」
09/09/18

2
'G; s / \ n / && /; / ^([-〜] * \ n)。* \ n \ 1 / d; s / \ n //; h; P 'は、おおよそ、「この行にホールドスペース全体を追加します。その後、重複した行がすべて表示される場合は、すべてを破棄します。それ以外の場合は、混乱全体をホールドスペースにコピーして、最初の部分を印刷します(これは、読む。」
ベータ版

その$!部分は必要ですか?sed 'N; /^\(.*\)\n\1$/!P; D'同じことをしませんか?私は自分のマシンで2つが異なる例を思いつくことはできません(最後に、両方のバージョンで空の行を試しましたが、どちらも問題ありませんでした)。
eddi、2012

1
ほぼ7年後、@ amichairには誰も答えませんでした... <sniff>は私を悲しくさせます。;)とにかく、[ -~]0x20(スペース)から0x7E(チルド)までの範囲のASCII文字を表します。これらを考慮している印刷可能な ASCII文字を(リンク先のページも0x7Fの/削除を持っているが、それはないと思えます)。そのため、ASCIIを使用していないユーザーや、たとえばタブ文字を使用しているユーザーにとっては、ソリューションが壊れてしまいます。[^\n]
Bレイヤ

14

@jonasのawkソリューションに似たPerlワンライナー:

perl -ne 'print if ! $x{$_}++' file

このバリエーションは、比較する前に末尾の空白を削除します。

perl -lne 's/\s*$//; print if ! $x{$_}++' file

このバリエーションは、ファイルをその場で編集します。

perl -i -ne 'print if ! $x{$_}++' file

このバリエーションは、ファイルをその場で編集し、バックアップを作成します file.bak

perl -i.bak -ne 'print if ! $x{$_}++' file

6

Andre Millerが上記で投稿したワンライナーは、入力ファイルが空白行で文字なしで終了するsedの最近のバージョンを除いて機能します。MacでCPUが回転するだけです。

最終行が空白で文字がない場合の無限ループ

sed '$!N; /^\(.*\)\n\1$/!P; D'

ハングしませんが、最後の行を失います

sed '$d;N; /^\(.*\)\n\1$/!P; D'

説明は、sed FAQの最後にあります。

GNUは、メンテナ移植性の問題にもかかわらず、と感じていたのsed
これは(というよりも、印刷するにはNコマンドを変更し、原因となる
パターンスペースは、自分の直感とより一致した削除)
どのように「次の行を追加」するコマンドはおよそべき行動します。
変更を支持するもう1つの事実
は、ファイルの行数が奇数の場合、「{N; command;}」は最終行を削除しますが、ファイルの行数が偶数の
場合、最終行を印刷します。

以前のNの動作(
EOFに到達するとパターンスペースを削除する)を使用したスクリプトを
、sedのすべてのバージョンと互換性のあるスクリプトに変換するには、単一の「N;」を変更します。「$ d; N;」に


5

Vim(Vi互換)を使用する別の方法

ファイルから重複する連続した行を削除します。

vim -esu NONE +'g/\v^(.*)\n\1$/d' +wq

ファイルから重複、非連続、空でない行を削除します。

vim -esu NONE +'g/\v^(.+)$\_.{-}^\1$/d' +wq


4

最初の解決策もhttp://sed.sourceforge.net/sed1line.txtからです

$ echo -e '1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5' |sed -nr '$!N;/^(.*)\n\1$/!P;D'
1
2
3
4
5

核となるアイデアは次のとおりです。

print ONLY once of each duplicate consecutive lines at its LAST appearance and use D command to implement LOOP.

説明:

  1. $!N;:現在の行が最後の行でない場合は、Nコマンドを使用して次の行をに読み込みますpattern space
  2. /^(.*)\n\1$/!P:currentの内容がで区切られてpattern spaceいる2つである場合、つまり次の行がwith current lineである場合、コアアイデアに従って印刷することはできません。それ以外の場合は、現在の行が重複するすべての連続する行の最後の外観であることを意味します。これで、コマンドを使用して現在のユーティリティで文字を印刷できます(これも印刷されます)。duplicate string\nsamePpattern space\n\n
  3. DDコマンドを使用して現在のpattern spaceユーティリティの文字を削除します\n(これ\nも削除されます)pattern space。次の行の内容です。
  4. そしてDcommandは強制的sedにそのFIRSTcommand にジャンプし$!Nますが、ファイルまたは標準入力ストリームから次の行を読みません。

2番目の解決策は(私自身から)理解しやすいです:

$ echo -e '1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5' |sed -nr 'p;:loop;$!N;s/^(.*)\n\1$/\1/;tloop;D'
1
2
3
4
5

核となるアイデアは次のとおりです。

print ONLY once of each duplicate consecutive lines at its FIRST appearance and use : command & t command to implement LOOP.

説明:

  1. 入力ストリームまたはファイルから新しい行を読み取り、それを1回出力します。
  2. :loopコマンドを使用して、label名前付きを設定しloopます。
  3. N次の行をに読み込むために使用しますpattern space
  4. s/^(.*)\n\1$/\1/次の行が現在の行と同じ場合、現在の行を削除するために使用し、sコマンドを使用してdeleteアクションを実行します。
  5. もし sコマンドが正常に実行されたコマンドtloopフォースsedを使用してlabel指定されたにジャンプしloopます。これにより、次の行に同じループが実行されlatest printedます。それ以外の場合は、と同じ行にDcommandを使用しdelete、最初のコマンドであるコマンドにlatest-printed line強制的sedにジャンプします。current pの内容はpattern space次の新しい行です。

Windowsでbusyboxが使用されている同じコマンド:busybox echo -e "1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5" | busybox sed -nr "$!N;/^(.*)\n\1$/!P;D"
スカベンジャー

-1

これはawk
Below Lineを使用して達成でき、一意の値を表示します

awk file_name | uniq

これらの一意の値を新しいファイルに出力できます

awk file_name | uniq > uniq_file_name

新しいファイルuniq_file_nameには重複のない一意の値のみが含まれます


-4
cat filename | sort | uniq -c | awk -F" " '$1<2 {print $2}'

awkを使用して重複行を削除します。


1
これは行の順序を乱します。
Vijay

1
20 GBのテキストファイルについて 遅すぎる。
Alexander Lubyagin 2017年

相変わらず、それcatは役に立たない。 とにかく、uniqこれはすでにそれ自体で既に行われており、入力が1行に正確に1ワードである必要はありません。
tripleee 2018年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.