重複行をペアで削除しますか?

重複行をペアで削除しますか?

今日このユースケースを見つけました。一見すると簡単に見えますが、遊んでみるとsort簡単ではないことがわかりuniqますsedawk

どのようにすべて削除できますか?行が重複していますか?つまり、特定の行に対して偶数の重複行がある場合はすべて削除し、奇数の重複行がある場合は1行だけ残してすべて削除します。 (ソートされた入力を想定できます。)

清潔でエレガントなソリューションは、より良い選択です。

入力例:

a
a
a
b
b
c
c
c
c
d
d
d
d
d
e

出力例:

a
d
e

答え1

sedこの質問を投稿した直後に回答が見つかりました。sedこれまで誰もこの質問を使用していないので、次のようになります。

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

より一般的な問題(3行、4行、または5行を削除するのはどうですか?)のより一般的な解決策は、次の拡張可能な解決策を提供します。

sed -e ':top' -e '$!{/\n/!{N;b top' -e '};};/^\(.*\)\n\1$/d;P;D' temp

展開して3行を削除します。

sed -e ':top' -e '$!{/\n.*\n/!{N;b top' -e '};};/^\(.*\)\n\1\n\1$/d;P;D' temp

または、クワッドラインを削除します。

sed -e ':top' -e '$!{/\n.*\n.*\n/!{N;b top' -e '};};/^\(.*\)\n\1\n\1\n\1$/d;P;D' temp

sedストリームで実際に動作できるという点で、他のほとんどのオプションと比較して追加の利点があり、重複しているかどうかを確認する実際の行数よりも多くのメモリ記憶領域は必要ありません。


〜のようにcuonglmがコメントで指摘しました、マルチバイト文字を含む行が誤って削除されないようにするには、ロケールをCに設定する必要があります。したがって、上記のコマンドは次のようになります。

LC_ALL=C sed '$!N;/^\(.*\)\n\1$/d;P;D' temp
LC_ALL=C sed -e ':top' -e '$!{/\n/!{N;b top' -e '};};/^\(.*\)\n\1$/d;P;D' temp
LC_ALL=C sed -e ':top' -e '$!{/\n.*\n/!{N;b top' -e '};};/^\(.*\)\n\1\n\1$/d;P;D' temp
# Etc.

答え2

非常にエレガントではありませんが、私が考えることができる最も簡単な方法は次のとおりです。

uniq -c input | awk '{if ($1 % 2 == 1) { print substr($0, 9) }}'

substr()は出力をトリミングしますuniq。これは、1行の反復回数が9,999,999を超えるまで機能します(この場合、uniqの出力は9文字にオーバーフローする可能性があります)。

答え3

awk次のスクリプトを試してください。

#!/usr/bin/awk -f
{
  if ((NR!=1) && (previous!=$0) && (count%2==1)) {
    print previous;
    count=0;
  }
  previous=$0;
  count++;
}
END {
  if (count%2==1) {
    print previous;
  }
}

lines.txtファイルがソートされたとします。

テストを受ける:

$ chmod +x script.awk
$ ./script.awk lines.txt
a
d
e

答え4

入力がソートされている場合:

perl -0pe  'while(s/^(.*)\n\1\n//m){}'

関連情報