sort -u ではなく sort | uniq を使おう

4 min

これは、TSG Advent Calendar 2025 の 9 日目の記事です。


Linux でデータの重複排除をする際、タイプ数が少ないので sort -u を使っていたが、特定の文字(ローマ数字や絵文字)が含まれる場合に集計結果がおかしいことに気づいた。

sort -u はロケールの影響を強く受けるため、厳密な重複排除には sort | uniq を使う方がよい。

Ⅰ, Ⅱ, Ⅲ が Ⅰ になってしまう

以下のようなローマ数字を含むリストを sort -u で処理すると、なぜか「Ⅰ」しか残らない。

$ sort -u <<EOF



EOF
# Ⅰ

GNU coreutils 8.32 環境で確認した。

原因は sort コマンドが参照している LC_COLLATE の設定にありそう。 挙動を確認したサーバーでは、以下のように設定されていた。

$  locale | grep COLLATE
LC_COLLATE="ja_JP.UTF-8"

sort -u は「ソート順序が同じなら重複とみなす」という挙動をする。 現在の設定だと、ローマ数字の1から3をすべて同じものとして扱っている。結果、最初の1行だけを残して他を削除してしまう。これはローマ数字に限らず、丸数字(①)でも同様の現象が起こる。

$ sort -u <<EOF



EOF
# ①

よって、sort -u はロケール設定によっては危険なので、他の方法を使うほうがよい。

方法1: uniq コマンドを使う

最も安全で確実な方法は、uniq コマンドを使うこと。

sort | uniq

sort は順番を比較した結果で重複排除しているのに対して、uniq は「完全に同じ行かどうか」を比較する。

方法2: LC_COLLATE=C を指定する

sort を使いたい場合は、ロケールを C(バイト順)に強制することで回避できる。

LC_COLLATE=C sort -u <<EOF



EOF
# Ⅰ
# Ⅱ
# Ⅲ

今回の場合だと、英語でも正常に扱ってくれるので、以下でもよかった。

$ LC_COLLATE="ja_JP.UTF-8" sort -u <<EOF



EOF
# ①
# ②
# ③

これなら文字コード順(バイト列の数値順)で比較されるため、正しく3行出力される。

積み残し

ということで、一見整合性のある説明はできた。 できたのだが、なんとなく他のサーバー(サーバーB)で以下のコマンドを打ったら、普通に3行出力されてしまった。

$ LC_COLLATE="ja_JP.UTF-8" sort -u <<EOF



EOF
# ①
# ②
# ③

日本語環境では変な挙動になる、という話だったのに、なぜか普通に動いてしまう。

サーバーの環境情報は以下の通りで、ほとんど同じだった。

サーバーA
$ cat /etc/os-release
VERSION="22.04.1 LTS (Jammy Jellyfish)"

$ ldd --version
ldd (Ubuntu GLIBC 2.35-0ubuntu3.11) 2.35

$ sort --version
sort (GNU coreutils) 8.32


サーバーB
$ cat /etc/os-release 
VERSION="22.04.4 LTS (Jammy Jellyfish)"

$ ldd --version
ldd (Ubuntu GLIBC 2.35-0ubuntu3.8) 2.35

$ sort --version
sort (GNU coreutils) 8.32

Ubuntu のマイナーバージョンの違いが影響しているのか、あるいは他の何らかの設定が影響しているのかは不明。 まあとりあえず sort | uniq を使うのが無難、という結論は変わらないし、LC_COLLATE=C を指定すれば挙動が変わったということも事実なので、今回はこれでよしとする。