Unixでファイル内の重複する行を削除する方法はありますか?
私はそれを行うことができますsort -u
し、uniq
コマンドが、私は使用したいですsed
かawk
。それは可能ですか?
awk
ますが、大きなファイルではかなりのリソースを消費します。
Unixでファイル内の重複する行を削除する方法はありますか?
私はそれを行うことができますsort -u
し、uniq
コマンドが、私は使用したいですsed
かawk
。それは可能ですか?
awk
ますが、大きなファイルではかなりのリソースを消費します。
回答:
awk '!seen[$0]++' file.txt
seen
Awkがファイルのすべての行を渡す連想配列です。行が配列にない場合、seen[$0]
評価はfalseになります。!
NOT論理演算子であるとtrueに偽を反転します。Awkは、式がtrueと評価される行を出力します。++
増分seen
ようにseen[$0] == 1
最初の時間の後の行は、その後発見されseen[$0] == 2
、等々 。
awkは、0
and ""
(空の文字列)以外をすべてtrueに評価します。重複行が中に配置されている場合seen
、その後!seen[$0]
はfalseと評価され、行は出力に書き込まれることはありません。
awk '!seen[$0]++' merge_all.txt > output.txt
for f in *.txt; do gawk -i inplace '!seen[$0]++' "$f"; done
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'
$!
部分は必要ですか?sed 'N; /^\(.*\)\n\1$/!P; D'
同じことをしませんか?私は自分のマシンで2つが異なる例を思いつくことはできません(最後に、両方のバージョンで空の行を試しましたが、どちらも問題ありませんでした)。
@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
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;」に 。
$ 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.
説明:
$!N;
:現在の行が最後の行でない場合は、N
コマンドを使用して次の行をに読み込みますpattern space
。/^(.*)\n\1$/!P
:currentの内容がで区切られてpattern space
いる2つである場合、つまり次の行がwith current lineである場合、コアアイデアに従って印刷することはできません。それ以外の場合は、現在の行が重複するすべての連続する行の最後の外観であることを意味します。これで、コマンドを使用して現在のユーティリティで文字を印刷できます(これも印刷されます)。duplicate string
\n
same
P
pattern space
\n
\n
D
:D
コマンドを使用して現在のpattern space
ユーティリティの文字を削除します\n
(これ\n
も削除されます)pattern space
。次の行の内容です。D
commandは強制的sed
にそのFIRST
command にジャンプし$!N
ますが、ファイルまたは標準入力ストリームから次の行を読みません。$ 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.
説明:
:loop
コマンドを使用して、label
名前付きを設定しloop
ます。N
次の行をに読み込むために使用しますpattern space
。s/^(.*)\n\1$/\1/
次の行が現在の行と同じ場合、現在の行を削除するために使用し、s
コマンドを使用してdelete
アクションを実行します。s
コマンドが正常に実行されたコマンドtloop
フォースsed
を使用してlabel
指定されたにジャンプしloop
ます。これにより、次の行に同じループが実行されlatest printed
ます。それ以外の場合は、と同じ行にD
commandを使用しdelete
、最初のコマンドであるコマンドにlatest-printed line
強制的sed
にジャンプします。current p
の内容はpattern space
次の新しい行です。busybox echo -e "1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5" | busybox sed -nr "$!N;/^(.*)\n\1$/!P;D"
cat filename | sort | uniq -c | awk -F" " '$1<2 {print $2}'
awkを使用して重複行を削除します。
cat
は役に立たない。 とにかく、uniq
これはすでにそれ自体で既に行われており、入力が1行に正確に1ワードである必要はありません。
uniq
は、単独で十分です。