【Linux】sortとuniqコマンドの使い方|重複削除・出現回数カウント・数値並び替えのパイプライン実践

コマンドリファレンス実務レシピ

アクセスログの集計やCSVデータの整理、システム運用の調査現場で、「データの重複をサクッと消したい」「どのIPアドレスやエラーが何回発生しているかランキングを作りたい」という場面は日常茶飯事です。手作業でExcelを開いてポチポチ集計するのは時間がもったいないですし、「手作業は負け」です。

Linux環境には、テキストを並び替える sort コマンドと、重複行の削除・カウントを行う uniq コマンドが標準で備わっています。この2つをパイプライン( | )で連結することで、数万〜数百万行の大規模データからでも一瞬で重複排除や出現頻度の集計が可能です。

本記事では、「なぜuniqの前にsortが必要なのか?」という基本原理から、実務で必須となる数値並び替え( -n )、逆順ソート( -r )、特定列の指定( -k )、重複カウント( uniq -c )、そして現場で即役立つログ解析ワンライナーまで体系的に解説します。

【結論】目的別ワンライナー早見表(コピペ用)

  • 重複行を削除して一覧化: sort file.txt | uniq (または sort -u file.txt
  • 各行の出現回数をカウント: sort file.txt | uniq -c
  • 出現回数の多い順に並び替え: sort file.txt | uniq -c | sort -rn
  • 重複している行だけを抽出: sort file.txt | uniq -d
  • 1度しか出現しない行だけを抽出: sort file.txt | uniq -u
  • CSVの特定列(第2列)を数値降順でソート: sort -t, -k2,2nr file.csv

sortとuniqコマンドの基本構文と主要オプション早見表

まずは、それぞれのコマンドの基本構文と、実務で頻繁に使う主要オプションを把握しておきましょう。

# sort コマンドの基本構文
sort [オプション] [ファイル名...]

# uniq コマンドの基本構文(標準入力またはファイルを指定)
uniq [オプション] [入力ファイル [出力ファイル]]

なぜuniqの前にsortが必要なのか?(隣接行比較の仕組み)

uniq コマンドを単体で実行して、「重複が消えない!」と戸惑った経験はありませんか?

実は、uniq コマンドは「隣接した連続行(直前の行)」しか比較しません。ファイル全体をメモリ上に保持して重複を探すのではなく、1行ずつストリームで読み込みながら「直前の行と完全に一致しているか」のみを判定する設計になっているためです。

以下のように、同じデータが離れた行に存在する場合、uniq 単体では重複を排除できません。

# サンプルデータ(重複が離れた位置にある)
$ cat fruits.txt
apple
orange
apple
banana
orange

# uniq 単体では重複行が削除されない
$ uniq fruits.txt
apple
orange
apple
banana
orange

# sort で事前に整列させてから uniq に渡すと正しく削除される
$ sort fruits.txt | uniq
apple
banana
orange

事前に sort を通すことで同一の文字列が連続して並ぶため、uniq が正しく重複を検出できるようになります。このため、「重複処理を行う際は sort と uniq をセットでパイプ接続する」のがLinux運用の大原則です。

sortの主要オプション(-n, -r, -k, -u)

sort コマンドには多数のオプションがありますが、実務で使うものは限られています。以下の表をマスターすれば日常業務の9割以上をカバーできます。

オプション 機能説明 実用例
-n 数値順ソート(文字列ではなく数値の大小として比較) sort -n data.txt
-r 逆順・降順ソート(大きい順、Z→A順に並び替え) sort -r list.txt
-k ソートキー(列)指定(特定の位置の列を基準にソート) sort -k 2,2 data.txt
-u 一意(ユニーク)化(ソートと同時に重複行を排除) sort -u list.txt
-t 区切り文字(デリミタ)指定(CSVのカンマやコロン等を指定) sort -t, -k1,1 file.csv
-h 人間可読な単位ソート(2K, 4M, 1G などのサイズ順) sort -h sizes.txt
-f 大文字・小文字を区別しない(Aとaを同一視) sort -f words.txt

※ 各オプションのより詳しい仕様やエッジケースは、「sortコマンドリファレンス」も併せて参照してください。

uniqの主要オプション(-c, -d, -u)

uniq コマンドは、重複行をただ削除するだけでなく、カウントや抽出の条件を変更できます。

オプション 機能説明 実用例
-c 出現回数をカウント(各行の先頭に出現回数を付与) sort log.txt | uniq -c
-d 重複している行のみ出力(2回以上現れた行を1行ずつ表示) sort list.txt | uniq -d
-D 重複している行をすべて出力(重複した行を元の個数分そのまま表示) sort list.txt | uniq -D
-u 重複していない行のみ出力(1度しか現れなかった行のみ抽出) sort list.txt | uniq -u
-i 大文字・小文字を区別しない(ABCとabcを同一と判定) sort -f words.txt | uniq -i

uniq の単体での詳しい挙動は、「uniqコマンドリファレンス」で確認できます。

実務で必須の並び替えパターン(sort)

ここからは、実務で誰もが一度はつまずく sort の重要パターンと落とし穴を具体的に見ていきます。

文字列ソートと数値ソート(-n)の違いと注意点

sort コマンドのデフォルトは、数値を「文字(辞書順・ASCIIコード順)」として評価します。そのため、オプションを付けないと以下のような意図しない並び順になります。

# 数値リストの用意
$ cat numbers.txt
100
20
3
1
2

# デフォルトの文字列ソート(1の次は100、その次に2が来る)
$ sort numbers.txt
1
100
2
20
3

# -n オプションを付けると「数値の大きさ」で正しくソートされる
$ sort -n numbers.txt
1
2
3
20
100

アクセスログのレスポンスタイム、ファイルサイズ、HTTPステータスコードなどをソートする際は、必ず -n オプションを指定するのが鉄則です。

また、df -hls -lh のように 1K, 500M, 2G といった単位が付いている場合は、-h(human-numeric-sort)オプションを使うと単位を正しく解釈して並び替えてくれます。

# 人間可読形式(K, M, G)のサイズ順ソート
$ sort -h -k5,5 du_output.txt

降順・逆順(-r)で並び替える

昇順(小さい順・A→Z)ではなく、降順(大きい順・Z→A)に並び替えるには -r(reverse)を指定します。

# 数値の降順ソート(大きい順ランキング)
$ sort -rn numbers.txt
100
20
3
2
1

実務では -r-n を組み合わせた -rn(または -nr)の形式が、アクセス数やエラー件数のランキング作成で最も頻繁に登場します。

CSV/TSVの特定列をキーにして並び替える(-k オプションと -t 区切り指定)

CSVやTSV、スペース区切りのログファイルでは、「第2列の数値を基準にソートしたい」「第3列の日時で並び替えたい」という要件が頻出します。

列の区切り文字には -t、並び替え基準列には -k(key)を使用します。

# サンプルCSV(ユーザー名,年齢,スコア)
$ cat users.csv
alice,30,85
bob,22,95
charlie,28,70
dave,22,88

# 第3列(スコア)を数値降順でソート
$ sort -t, -k3,3nr users.csv
bob,22,95
dave,22,88
alice,30,85
charlie,28,70

⚠️ 実務の超重要注意点:-k は必ず「開始列,終了列」で指定する!

多くの人が sort -k2 file.txt のように単一の数字で指定してしまいますが、これは「第2列から行末までの全フィールドをキーにする」という意味になります。意図通りに第2列「だけ」をキーにするには、必ず -k 2,2 と開始列と終了列を同一に指定してください。

複数列を条件にソートすることも可能です。例えば「第2列(年齢)を数値昇順、年齢が同じなら第3列(スコア)を数値降順」にする場合は以下のように記述します。

# 第2列昇順(数値) → 同値なら第3列降順(数値)
$ sort -t, -k2,2n -k3,3nr users.csv
dave,22,88
bob,22,95
charlie,28,70
alice,30,85

なお、タブ区切り(TSV)を -t で指定する場合は、BashのANSI-Cクォート記法を用いて -t $'t' と指定するのが確実です。

# TSVファイルのソート
$ sort -t $'t' -k2,2nr data.tsv

※ CSVの高度な加工や列抽出については、「BashでCSVを扱う方法まとめ」および「CSV加工はどこまでbashでできるのか」もご参照ください。

重複行の処理と集計パターン(uniq)

整列させたテキストに対して uniq を適用することで、重複の排除や統計分析を強力に進めることができます。

重複行を1行にまとめる(重複排除)

最も基本的な使い方は、重複行を1つに集約して一意な一覧を作成するパターンです。

# パイプラインでの重複排除
$ sort items.txt | uniq

# sort コマンド単体の -u オプションでも同一の結果が得られる
$ sort -u items.txt

💡 sort -u と sort | uniq はどちらを使うべき?

  • 単に重複を消したいだけの場合: sort -u の方がプロセス起動が1つで済み、高速かつパイプのオーバーヘッドがありません。
  • 出現回数のカウントや重複抽出を行う場合: uniq -cuniq -d など豊富なオプションを持つ uniq とのパイプラインが必要です。

各行の出現回数をカウントする(uniq -c)

実務で最も活躍するのが uniq -c です。各行の先頭に出現回数を付与して出力してくれます。

# サンプルデータ
$ cat server_status.txt
200
500
200
200
404
500

# 出現回数をカウント
$ sort server_status.txt | uniq -c
      3 200
      1 404
      2 500

出力結果の先頭にスペースと数値が付与されるため、さらに sort -rn に繋ぐことで、「最も多く発生したエラーやステータスコードのランキング」を瞬時に作成できます。

# 出現回数の多い順に並び替え(TOP集計)
$ sort server_status.txt | uniq -c | sort -rn
      3 200
      2 500
      1 404

重複している行だけ / 重複していない行だけを抽出する(-d / -u)

データクレンジングや異常検知において、「重複が発生している異常なレコードだけを見つけたい」「1回しか登場しないユニークなデータだけを取り出したい」というケースがあります。

# サンプルリスト
$ cat email_list.txt
a@example.com
b@example.com
a@example.com
c@example.com
b@example.com
d@example.com

# 【-d】2回以上現れた「重複行のみ」を抽出(重複アドレスの特定)
$ sort email_list.txt | uniq -d
a@example.com
b@example.com

# 【-u】1度しか現れなかった「ユニーク行のみ」を抽出
$ sort email_list.txt | uniq -u
c@example.com
d@example.com

IDの重複チェックや会員リストの突き合わせなどで、grepやスクリプトを書かずに1行で確認できる非常に便利な機能です。

ログ解析の鉄板パイプライン(実践ワンライナー)

Linux管理者が現場で毎日叩いている、実践的なパイプラインを構築してみましょう。

アクセスログからアクセス元IPアドレスTOP10を抽出する(cut | sort | uniq -c | sort -rn | head -n 10)

Webサーバー(Nginx / Apache)へのアクセス集中やDoS攻撃が疑われる際、真っ先に実行する調査コマンドがこちらです。

# Nginx/ApacheのCombinedログからアクセス元IPアドレスTOP10を抽出
cut -d ' ' -f 1 /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -n 10

このパイプラインの内部処理を分解すると以下のようになります:

  1. cut -d ' ' -f 1 : 空白区切りの第1フィールド(アクセス元IPアドレス)だけを抽出。
  2. sort : IPアドレス文字列を昇順ソートし、同一IPを連続した行にまとめる。
  3. uniq -c : 連続した同一IPの行数を集計し、[件数] [IP] の形式で出力。
  4. sort -rn : 第1列の「件数」を数値(-n)の降順(-r、多い順)でソート。
  5. head -n 10 : ランキングの上位10件だけを切り出して画面表示。
# 実行結果例
   4521 192.168.1.50
   3210 10.0.0.12
   1890 203.0.113.195
    854 198.51.100.4
    620 192.0.2.88
    ...

もし特定URLへのリクエスト数やステータスコードを抽出したい場合は、cut の代わりに awk を使うとさらに柔軟に抽出できます。

# awk と連携して HTTP 500 番台エラーを出したURLのランキング TOP5 を抽出
awk '$9 ~ /^5/ {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -n 5

※ 各種フィルタリングや列抽出の詳細は、「awkコマンドの使い方完全ガイド」および「cutコマンドの使い方」も参考にしてください。

実務でハマりやすい落とし穴と高速化テクニック

LC_ALL=C によるソートの高速化と挙動安定化

実務で数ギガバイトに及ぶ巨大なログファイルをソートすると、処理に非常に長い時間がかかることがあります。その原因の多くは「環境変数 LANG / LC_ALL のロケール設定(UTF-8)」にあります。

ロケールが ja_JP.UTF-8en_US.UTF-8 になっていると、ソート処理時に辞書順の照合順序(Collation)ルールが適用され、多言語対応の重い文字比較計算が走ります。

# LC_ALL=C を前置してバイト単位でソート(数倍〜十数倍高速化)
LC_ALL=C sort huge_log.txt | uniq -c | LC_ALL=C sort -rn

LC_ALL=C を付与すると、純粋なバイトコード(ASCIIコード)順で比較されるため、処理速度が3倍〜10倍以上劇的に高速化し、環境によるソート順のブレも完全に解消されます。

巨大ファイルでのメモリ不足対策(-S と -T)

sort はメモリ内にデータを読み込んで処理を行いますが、利用可能メモリを超える巨大ファイルの場合、ディスク上に一時ファイルを作成します。

# メモリ使用量を明示的に指定(例: 2GB)
sort -S 2G data.txt

# 一時ファイルの保存先ディレクトリを容量に余裕のあるディスクに変更
sort -T /data/tmp -S 4G huge_data.txt

デフォルトの /tmp が tmpfs(メモリファイルシステム)等で容量が小さい環境では、ディスクフルで sort: write failed: /tmp/...: No space left on device エラーが発生しやすいため、-T オプションを覚えておくと安心です。

まとめ & 関連記事

sortuniq は、Linux環境においてデータ処理の基盤となる超重要コマンドです。手作業での加工や重いスクリプトを書く前に、まずはこの2つのパイプラインで解決できないか検討してみてください。

📌 本記事の重要ポイント3選

  • uniq の前には必ず sort を実行する: uniq は隣接行しか比較しないため、整列が必須。
  • 数値ソートは -n、逆順は -r、列指定は -k N,N: 特に -k 2,2 のように終了列を明示するのが安全。
  • 集計は uniq -c | sort -rn のコンボが最強: 出現回数をカウントし、即座にランキング化できる。

あわせて読みたい関連記事

Bash玄

はじめまして!Bash玄です。

エンジニアとしてシステム運用に携わる中で、手作業の多さに限界を感じ、Bashスクリプトを活用して業務を効率化したのがきっかけで、この道に入りました。「手作業は負け」「スクリプトはシンプルに」をモットーに、誰でも実践できるBashスクリプトの書き方を発信しています。

このサイトでは、Bashの基礎から実践的なスクリプト作成まで、初心者でもわかりやすく解説しています。少しでも「Bashって便利だな」と思ってもらえたら嬉しいです!

# 好きなこと
- シンプルなコードを書くこと
- コマンドラインを快適にカスタマイズすること
- 自動化で時間を生み出すこと

# このサイトを読んでほしい人
- Bashに興味があるけど、何から始めればいいかわからない人
- 定型業務を自動化したい人
- 効率よくターミナルを使いこなしたい人

Bashの世界に一歩踏み出して、一緒に「Bash道」を極めていきましょう!

Bash玄をフォローする

コメント