並べ替えられた一意のリストを取得する必要がある誰かをどこでも見かけますが、それらは常にsort | uniq
にパイプします。誰かが代わりにsort -u
を使用する例を見たことがありません。何故なの?違いは何ですか、なぜ一意のフラグよりもuniqを使用した方が良いのですか?
sort | uniq
はsort -u
の前に存在し、幅広いシステムと互換性がありますが、ほとんどすべての最近のシステムは-u
をサポートしています-POSIXです。これは主にsort -u
が存在しなかった時代への逆戻りです(そして、人々が知っている方法が引き続き機能する場合、メソッドを変更する傾向はありません。ifconfig
とip
の採用を見てください)。
ファイル内の重複を削除するにはソートが必要であるため(少なくとも標準的なケースでは)、2つはマージされた可能性が高く、ソートの非常に一般的な使用例です。また、両方の操作を同時に実行できるため、内部的にも高速です(uniq
とsort
の間にIPCを必要としないため)。特に、ファイルが大きい場合、sort -u
はデータのソートに使用する中間ファイルの数が少なくなる可能性があります。
私のシステムでは、常に次のような結果が得られます。
$ dd if=/dev/urandom of=/dev/shm/file bs=1M count=100
100+0 records in
100+0 records out
104857600 bytes (105 MB) copied, 8.95208 s, 11.7 MB/s
$ time sort -u /dev/shm/file >/dev/null
real 0m0.500s
user 0m0.767s
sys 0m0.167s
$ time sort /dev/shm/file | uniq >/dev/null
real 0m0.772s
user 0m1.137s
sys 0m0.273s
また、重要である可能性があるsort
の戻りコードをマスクしません(最近のシェルでは、これを取得する方法があります。たとえば、bash
の$PIPESTATUS
配列がありますが、常にそうであるとは限りません)。
1つの違いは、uniq
には、比較のためにフィールドをスキップしたり、値の繰り返し数をカウントするなど、いくつかの便利な追加オプションがあることです。 sort
の-u
フラグは、装飾されていないuniq
コマンドの機能のみを実装します。
POSIX準拠のsort
sおよびuniq
s(GNU uniq
は現在その点で準拠していません)では、 違いがあります そのsort
は、ロケールの照合アルゴリズムを使用して文字列を比較します(通常、 strcoll()
は文字列を比較します)、uniq
はバイト値のIDをチェックします(通常はstrcmp()
を使用します)¹。
これは、少なくとも2つの理由で重要です。
一部のロケール、特にGNUシステムでは、同じ文字を並べ替えるさまざまな文字があります。たとえば、GNUシステムのen_US.UTF-8ロケールでは、すべての①②③④⑤⑥⑦⑧⑨⑩...文字²と他の多くのソート順が定義されていないため、同じようにソートされます。 0123456789のアラビア数字は、 東部アラビアアラビア語 の対応するもの(٠١٢٣٤٥٦٧٨٩)と同じようにソートされます。
_sort -u
_の場合、①は②と同じようにソートされ、0123はasと同じようにソートされるため、_sort -u
_はそれぞれ1つしか保持しませんが、uniq
(GNUではない)uniq
は、strcoll()
を使用します(例外_-i
_))の場合、①は②とは異なり、0123はfromとは異なるため、uniq
は4つすべてを一意と見なします。
strcoll
は、有効な文字の文字列のみを比較できます(入力に有効な文字を形成しないバイトシーケンスがある場合、動作はPOSIXで定義されていません)。一方、strcmp()
は、バイトとバイトの比較のみを行うため、文字を考慮しません。つまり、これが_sort -u
_が有効なテキストを形成しない場合にすべての一意の行を提供しない可能性があるもう1つの理由です。 _sort|uniq
_は、非テキスト入力ではまだ指定されていませんが、そのため、実際には固有の行が表示される可能性が高くなります。これらの微妙なことに加えて、これまで指摘されていないことの1つは、uniq
が行全体を字句的に比較するのに対し、sort
の_-u
_はコマンドラインで指定されたソート指定に基づいて比較することです。
_$ printf '%s\n' 'a b' 'a c' | sort -uk 1,1
a b
$ printf '%s\n' 'a b' 'a c' | sort -k 1,1 | uniq
a b
a c
$ printf '%s\n' 0 -0 +0 00 '' | sort -n | uniq
0
-0
+0
00
$ printf '%s\n' 0 -0 +0 00 '' | sort -nu
0
_
¹以前のバージョンのPOSIX仕様は混乱を引き起こしていましたが、_LC_COLLATE
_変数をuniq
に影響を与える変数としてリストすることで、2018エディションで削除され、上記の説明に従って動作が明確になりました。 対応するAustinグループのバグを参照してください
² 2019 edit 。それ以降は修正されていますが、 GNU libc のバージョン2.30では、95%以上のUnicodeコードポイントの順序が未定義のままです。 ????????????????????でテストできます。代わりに、例えば新しいバージョンで
sort | uniq
を使用することをお勧めします。-u
(重複を排除する)オプションを使用して、大文字と小文字が混在する文字列を含む重複を削除しようとすると、結果を理解するのが容易ではないためです。
注:以下の例を実行する前に、以下を実行して標準のC照合シーケンスをシミュレートする必要があります。
LC_ALL=C
export LC_ALL
たとえば、ファイルを並べ替えて重複を削除すると同時に、文字列の大文字と小文字を区別したい場合などです。
$ cat short #file to sort
Pear
Pear
Apple
pear
Apple
$ sort short #normal sort (in normal C collating sequence)
Apple #the lower case words are at the end
Pear
Pear
Apple
pear
$ sort -f short #correctly sorts ignoring the C collating order
Apple #but duplicates are still there
Apple
Pear
Pear
pear
$ sort -fu short #By adding the -u option to remove duplicates it is
Apple #difficult to ascertain the logic that sort uses to remove
Pear #duplicates(i.e., why did it remove pear instead of Pear?)
この混乱は、-u
オプションを使用して重複を削除しないことで解決されます。 uniq
の使用はより予測可能です。以下では、最初に大文字と小文字を並べ替えて無視し、それをuniq
に渡して重複を削除します。
$ sort -f short | uniq
Apple
apple
Pear
pear
今日わかったもう1つの違いは、sort -u
が一意のフラグを並べ替える列にのみ適用する区切り文字に基づいて並べ替える場合です。
$ cat input.csv
3,World,1
1,Hello,1
2,Hello,1
$ cat input.csv | sort -t',' -k2 -u
1,Hello,1
3,World,1
$ cat input.csv | sort -t',' -k2 | uniq
1,Hello,1
2,Hello,1
3,World,1