はじめに

サーバー運用やデータ処理の現場では、ログファイルやCSVデータを集計・分析する場面が頻繁に発生します。GUIツールを起動するまでもない軽微な集計作業や、定期的なレポート作成の自動化において、Linuxのコマンドラインツールは非常に強力です。

本記事では、データの並べ替え、重複処理、カウント、列抽出、結合といった基本的なデータ操作を行うコマンド群を体系的に解説します。これらのコマンドをパイプで組み合わせることで、複雑な集計処理もワンライナーで実現できるようになります。

前提知識として、パイプとリダイレクトの基本、およびgrepコマンドの基礎を理解していると学習がスムーズです。必要に応じて関連記事も参照してください。

動作確認環境

本記事のコマンドは以下の環境で動作確認しています。

項目 内容
OS Ubuntu 24.04 LTS
シェル bash 5.3
coreutils 9.4

WSL2やその他の主要ディストリビューション(AlmaLinux、Debian等)でも同様に動作します。macOSではBSD版のコマンドがインストールされているため、一部オプションが異なる場合があります。

サンプルデータの準備

本記事で使用するサンプルデータを準備します。実際に手を動かしながら学習する場合は、以下のコマンドでファイルを作成してください。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 作業ディレクトリの作成
mkdir -p ~/linux-practice && cd ~/linux-practice

# 売上データ(CSV形式)
cat << 'EOF' > sales.csv
date,product,category,amount,quantity
2025-01-15,Keyboard,Electronics,3500,2
2025-01-15,Mouse,Electronics,1200,3
2025-01-16,Notebook,Stationery,450,10
2025-01-16,Pen,Stationery,150,20
2025-01-17,Monitor,Electronics,28000,1
2025-01-17,Keyboard,Electronics,3500,1
2025-01-18,Notebook,Stationery,450,5
2025-01-18,USB Cable,Electronics,800,4
2025-01-19,Mouse,Electronics,1200,2
2025-01-19,Pen,Stationery,150,15
EOF

# アクセスログ(簡易形式)
cat << 'EOF' > access.log
192.168.1.10 - - [15/Jan/2025:10:15:32] "GET /index.html HTTP/1.1" 200 1234
192.168.1.20 - - [15/Jan/2025:10:16:45] "GET /about.html HTTP/1.1" 200 2345
192.168.1.10 - - [15/Jan/2025:10:17:12] "GET /products.html HTTP/1.1" 200 3456
192.168.1.30 - - [15/Jan/2025:10:18:23] "POST /api/login HTTP/1.1" 401 123
192.168.1.10 - - [15/Jan/2025:10:19:45] "GET /contact.html HTTP/1.1" 200 1567
192.168.1.20 - - [15/Jan/2025:10:20:01] "GET /index.html HTTP/1.1" 200 1234
192.168.1.40 - - [15/Jan/2025:10:21:33] "GET /products.html HTTP/1.1" 404 234
192.168.1.10 - - [15/Jan/2025:10:22:15] "GET /index.html HTTP/1.1" 200 1234
192.168.1.30 - - [15/Jan/2025:10:23:44] "POST /api/login HTTP/1.1" 200 456
192.168.1.50 - - [15/Jan/2025:10:24:56] "GET /admin.html HTTP/1.1" 403 789
EOF

# ユーザーリスト
cat << 'EOF' > users.txt
alice Engineering
bob Sales
charlie Engineering
david Marketing
eve Sales
frank Engineering
grace Marketing
EOF

# 部署情報
cat << 'EOF' > departments.txt
Engineering Tokyo
Marketing Osaka
Sales Nagoya
HR Fukuoka
EOF

sort - データの並べ替え

sortコマンドは、テキストファイルの行を指定した条件で並べ替えます。ログ分析やレポート作成において、データを特定の順序で整理することは基本中の基本です。

基本的な使い方

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# アルファベット順にソート
sort users.txt

# 出力例
# alice Engineering
# bob Sales
# charlie Engineering
# david Marketing
# eve Sales
# frank Engineering
# grace Marketing

デフォルトでは、各行を文字列として比較し、辞書順(アルファベット順)に並べ替えます。

主要オプション

sortコマンドには多くのオプションがあります。用途別に整理して紹介します。

ソート順序の制御

オプション 説明
-r 逆順(降順)でソート
-n 数値としてソート
-h 人間が読みやすい数値(1K、2G等)でソート
-M 月名(JAN、FEB等)でソート
-V バージョン番号としてソート
-R ランダムにシャッフル

比較方法の制御

オプション 説明
-f 大文字・小文字を区別しない
-b 先頭の空白を無視
-d 空白と英数字のみで比較(辞書順)
-i 印字可能文字のみで比較

キー指定と区切り文字

オプション 説明
-k ソートキー(フィールド)を指定
-t フィールドの区切り文字を指定
-u 重複行を除去(uniqと同等)

数値ソート

数値を含むデータを正しくソートするには、-n オプションが必須です。

1
2
3
4
5
6
7
# 文字列としてソート(誤った結果)
echo -e "10\n2\n100\n20\n1" | sort
# 出力: 1, 10, 100, 2, 20(辞書順)

# 数値としてソート(正しい結果)
echo -e "10\n2\n100\n20\n1" | sort -n
# 出力: 1, 2, 10, 20, 100

人間が読みやすい形式(1K、2M、3G等)の数値をソートする場合は -h オプションを使用します。

1
2
# ディスク使用量をサイズ順にソート
du -sh /var/log/* 2>/dev/null | sort -h

特定のフィールドでソート

CSVやTSVなどの構造化データでは、特定の列(フィールド)を基準にソートすることが多くなります。-t で区切り文字を指定し、-k でソートキーを指定します。

1
2
3
4
5
6
7
8
# CSVの4列目(金額)で数値ソート(ヘッダー除外)
tail -n +2 sales.csv | sort -t',' -k4 -n

# 出力例
# 2025-01-16,Pen,Stationery,150,20
# 2025-01-19,Pen,Stationery,150,15
# 2025-01-16,Notebook,Stationery,450,10
# ...

-k オプションの書式は F[.C][OPTS][,F[.C][OPTS]] です。

  • F: フィールド番号(1から開始)
  • .C: フィールド内の文字位置(省略可)
  • OPTS: このキーに適用するオプション(b、d、f、i、n、r等)
1
2
# 3列目(カテゴリ)で昇順、4列目(金額)で降順にソート
tail -n +2 sales.csv | sort -t',' -k3,3 -k4,4nr

複合ソートの例

複数のキーを組み合わせた実践的なソート例です。

1
2
3
4
5
6
7
8
9
# カテゴリごとに金額の高い順に並べる
tail -n +2 sales.csv | sort -t',' -k3,3 -k4,4nr

# 出力例
# 2025-01-17,Monitor,Electronics,28000,1
# 2025-01-15,Keyboard,Electronics,3500,2
# 2025-01-17,Keyboard,Electronics,3500,1
# 2025-01-15,Mouse,Electronics,1200,3
# ...

ロケールの影響

sortコマンドの動作は、環境変数 LC_COLLATE の影響を受けます。予期しない結果を避けるため、スクリプトでは LC_ALL=C を設定することをお勧めします。

1
2
3
4
5
# ロケールに依存しないソート
LC_ALL=C sort data.txt

# 現在のロケール設定を確認
locale | grep LC_COLLATE

uniq - 重複行の処理

uniqコマンドは、連続する重複行を検出し、除去またはカウントします。ログのIPアドレス集計やデータのクレンジングで頻繁に使用されます。

重要な前提条件

uniqコマンドは隣接する行同士を比較します。つまり、ファイル全体から重複を除去するには、事前にsortでソートしておく必要があります。

1
2
3
4
5
6
7
# ソートせずにuniqを使用(一部の重複が残る)
echo -e "apple\nbanana\napple\norange\nbanana" | uniq
# 出力: apple, banana, apple, orange, banana

# ソートしてからuniqを使用(正しく重複除去)
echo -e "apple\nbanana\napple\norange\nbanana" | sort | uniq
# 出力: apple, banana, orange

なお、sort -u を使えば、ソートと重複除去を1つのコマンドで実行できます。

主要オプション

オプション 説明
-c 各行の出現回数を先頭に表示
-d 重複している行のみ表示(1行だけ)
-D 重複している行をすべて表示
-u 重複していない(ユニークな)行のみ表示
-i 大文字・小文字を区別しない
-f N 先頭のN個のフィールドを無視して比較
-s N 先頭のN文字を無視して比較
-w N 先頭のN文字のみで比較

出現回数のカウント

-c オプションは、各行が何回出現したかをカウントします。アクセスログの集計で特に有用です。

1
2
3
4
5
6
7
8
9
# アクセスログからIPアドレスを抽出してカウント
cut -d' ' -f1 access.log | sort | uniq -c

# 出力例
#       4 192.168.1.10
#       2 192.168.1.20
#       2 192.168.1.30
#       1 192.168.1.40
#       1 192.168.1.50

出現回数でさらにソートすることで、アクセス数の多いIPアドレスを特定できます。

1
2
3
4
5
6
7
8
9
# アクセス数の多い順にソート
cut -d' ' -f1 access.log | sort | uniq -c | sort -rn

# 出力例
#       4 192.168.1.10
#       2 192.168.1.30
#       2 192.168.1.20
#       1 192.168.1.50
#       1 192.168.1.40

重複行・ユニーク行の抽出

1
2
3
4
5
6
7
# 重複している行のみ表示
echo -e "apple\nbanana\napple\norange" | sort | uniq -d
# 出力: apple

# ユニークな行(重複していない行)のみ表示
echo -e "apple\nbanana\napple\norange" | sort | uniq -u
# 出力: banana, orange

フィールドを無視した比較

ログファイルなどでは、タイムスタンプ部分を無視して重複を検出したい場合があります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 最初の5フィールドを無視して比較(アクセスログのパス部分で重複検出)
awk '{print $7}' access.log | sort | uniq -c | sort -rn

# 出力例
#       3 /index.html
#       2 /products.html
#       2 /api/login
#       1 /contact.html
#       1 /admin.html
#       1 /about.html

wc - 行・単語・文字のカウント

wcコマンド(word count)は、ファイルの行数、単語数、バイト数をカウントします。シンプルですが、スクリプトでの条件分岐やレポート作成に欠かせないコマンドです。

基本的な使い方

1
2
3
4
5
# 行数、単語数、バイト数を表示
wc sales.csv

# 出力例
#  11  11 389 sales.csv

出力は「行数 単語数 バイト数 ファイル名」の順序で表示されます。

主要オプション

オプション 説明
-l 行数のみ表示
-w 単語数のみ表示
-c バイト数のみ表示
-m 文字数のみ表示(マルチバイト文字対応)
-L 最長行の長さを表示

実践的な使用例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# ログファイルの行数をカウント
wc -l access.log
# 出力: 10 access.log

# パイプと組み合わせて条件に一致する行数をカウント
grep "200" access.log | wc -l
# 出力: 6

# 複数ファイルの合計行数
wc -l *.txt
# 出力例
#   7 users.txt
#   4 departments.txt
#  11 total

パイプでの活用

wcコマンドは、パイプと組み合わせてフィルタリング結果の件数を確認するのに便利です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# エラーログの件数を確認
grep -E "401|403|404" access.log | wc -l
# 出力: 3

# ユニークなIPアドレスの数
cut -d' ' -f1 access.log | sort -u | wc -l
# 出力: 5

# Electronics カテゴリの商品数
grep "Electronics" sales.csv | wc -l
# 出力: 6

cut - 列(フィールド)の抽出

cutコマンドは、各行から指定した部分(バイト位置、文字位置、またはフィールド)を抽出します。CSVやTSVデータから必要な列だけを取り出す際に重宝します。

主要オプション

オプション 説明
-b LIST 指定したバイト位置を抽出
-c LIST 指定した文字位置を抽出
-f LIST 指定したフィールドを抽出
-d DELIM フィールドの区切り文字を指定(デフォルトはタブ)
-s 区切り文字を含まない行を出力しない
–complement 指定した部分以外を出力
–output-delimiter 出力時の区切り文字を指定

範囲指定の書式

LISTには以下の形式で範囲を指定できます。

形式 説明
N N番目のみ
N- N番目から行末まで
N-M N番目からM番目まで
-M 先頭からM番目まで
N,M N番目とM番目(複数指定)

フィールドの抽出

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# CSVから2列目(商品名)を抽出
cut -d',' -f2 sales.csv

# 出力例
# product
# Keyboard
# Mouse
# Notebook
# ...

# 複数のフィールドを抽出(1列目と4列目)
cut -d',' -f1,4 sales.csv

# 出力例
# date,amount
# 2025-01-15,3500
# 2025-01-15,1200
# ...

# 2列目から4列目までを抽出
cut -d',' -f2-4 sales.csv

アクセスログからの情報抽出

cutコマンドは、ログファイルから必要な情報を抽出する際に活躍します。

1
2
3
4
5
6
7
8
# IPアドレスを抽出(スペース区切りの1番目のフィールド)
cut -d' ' -f1 access.log

# HTTPステータスコードを抽出(スペース区切りの9番目)
cut -d' ' -f9 access.log

# リクエストパスを抽出(スペース区切りの7番目)
cut -d' ' -f7 access.log

文字位置での抽出

固定長データや特定の位置にある情報を抽出する場合は、-c オプションを使用します。

1
2
3
4
5
6
7
# 日付の年部分のみ抽出(1〜4文字目)
cut -d',' -f1 sales.csv | tail -n +2 | cut -c1-4

# 出力: 2025(すべて同じ年)

# 日付の月部分のみ抽出(6〜7文字目)
cut -d',' -f1 sales.csv | tail -n +2 | cut -c6-7

出力区切り文字の変更

抽出した結果の区切り文字を変更できます。

1
2
3
4
5
6
7
# カンマ区切りをタブ区切りに変換
cut -d',' -f1-3 sales.csv --output-delimiter=$'\t'

# 出力例
# date    product    category
# 2025-01-15    Keyboard    Electronics
# ...

paste - 行の結合

pasteコマンドは、複数のファイルの対応する行を横に結合します。異なるファイルのデータを1行にまとめたい場合に使用します。

基本的な使い方

1
2
3
4
5
6
7
# 2つのファイルを横に結合(タブ区切り)
paste users.txt departments.txt

# 1ファイルを複数列に変換
echo -e "a\nb\nc\nd\ne\nf" | paste - - -
# 出力: a    b    c
#       d    e    f

主要オプション

オプション 説明
-d LIST 区切り文字を指定(デフォルトはタブ)
-s 各ファイルを1行ずつ処理(縦を横に変換)

区切り文字の指定

1
2
3
4
5
6
# カンマで結合
paste -d',' file1.txt file2.txt

# 複数の区切り文字を順番に使用
paste -d':,' file1.txt file2.txt file3.txt
# 1行目: file1の1行目:file2の1行目,file3の1行目

縦データを横に変換

-s オプションを使用すると、ファイルの各行を横に並べて1行にまとめます。

1
2
3
# ユーザー名を1行にまとめる
cut -d' ' -f1 users.txt | paste -s -d','
# 出力: alice,bob,charlie,david,eve,frank,grace

標準入力との組み合わせ

-(ハイフン)は標準入力を表します。これを利用して、1つの入力を複数列に分割できます。

1
2
3
4
5
6
7
8
9
# 1列のデータを3列に変換
seq 1 9 | paste - - -
# 出力:
# 1    2    3
# 4    5    6
# 7    8    9

# 連番とデータを横に結合
cut -d' ' -f1 users.txt | nl | paste - <(cut -d' ' -f2 users.txt)

join - 共通フィールドでの結合

joinコマンドは、2つのファイルを共通のフィールド(キー)で結合します。SQLのJOIN操作に相当する処理をコマンドラインで実現できます。

重要な前提条件

joinコマンドを使用する前に、両方のファイルを結合キーでソートしておく必要があります。

基本的な使い方

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# ユーザーリストと部署情報を結合
# まず、結合キー(部署名)でソート
sort -k2 users.txt > users_sorted.txt
sort -k1 departments.txt > departments_sorted.txt

# 2番目のフィールド(users)と1番目のフィールド(departments)で結合
join -1 2 -2 1 users_sorted.txt departments_sorted.txt

# 出力例
# Engineering alice Tokyo
# Engineering charlie Tokyo
# Engineering frank Tokyo
# Marketing david Osaka
# Marketing grace Osaka
# Sales bob Nagoya
# Sales eve Nagoya

主要オプション

オプション 説明
-1 FIELD FILE1の結合キーフィールド番号
-2 FIELD FILE2の結合キーフィールド番号
-j FIELD 両ファイル共通の結合キーフィールド番号
-t CHAR フィールド区切り文字
-a FILENUM マッチしない行も出力(LEFT/RIGHT JOIN)
-v FILENUM マッチしない行のみ出力
-o FORMAT 出力フォーマットを指定
-e STRING 欠損値を指定した文字列で埋める
-i 大文字・小文字を区別しない

LEFT JOINの実現

-a オプションを使用すると、マッチしない行も出力できます。

1
2
# users_sorted.txtのすべての行を出力(部署情報がない場合も)
join -1 2 -2 1 -a 1 users_sorted.txt departments_sorted.txt

出力フォーマットの指定

-o オプションで、出力するフィールドと順序を指定できます。

1
2
3
4
5
6
7
8
# ファイル番号.フィールド番号 の形式で指定
# 0は結合キーを表す
join -1 2 -2 1 -o 1.1,0,2.2 users_sorted.txt departments_sorted.txt

# 出力例
# alice Engineering Tokyo
# charlie Engineering Tokyo
# ...

実践:ログ集計とレポート作成

ここまで学んだコマンドを組み合わせて、実践的なログ集計を行います。

IPアドレス別アクセス数ランキング

1
2
3
4
5
6
7
8
9
# アクセスログからIPアドレスを抽出し、アクセス数でランキング
cut -d' ' -f1 access.log | sort | uniq -c | sort -rn | head -5

# 出力例
#       4 192.168.1.10
#       2 192.168.1.30
#       2 192.168.1.20
#       1 192.168.1.50
#       1 192.168.1.40

HTTPステータスコード別の集計

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# ステータスコードごとのリクエスト数
cut -d' ' -f9 access.log | sort | uniq -c | sort -rn

# 出力例
#       6 200
#       1 404
#       1 403
#       1 401
#       1 200

# エラー(4xx、5xx)のみ抽出して集計
cut -d' ' -f9 access.log | grep -E "^[45]" | sort | uniq -c

# 出力例
#       1 401
#       1 403
#       1 404

カテゴリ別売上集計

1
2
3
4
5
6
# カテゴリごとの商品数
tail -n +2 sales.csv | cut -d',' -f3 | sort | uniq -c

# 出力例
#       6 Electronics
#       4 Stationery

リクエストパス別アクセス数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# リクエストパスを抽出してランキング作成
cut -d' ' -f7 access.log | sort | uniq -c | sort -rn

# 出力例
#       3 /index.html
#       2 /products.html
#       2 /api/login
#       1 /contact.html
#       1 /admin.html
#       1 /about.html

日別アクセス数の集計

1
2
3
4
5
# 日付部分を抽出して集計
grep -oP '\d{2}/\w+/\d{4}' access.log | sort | uniq -c

# 出力例
#      10 15/Jan/2025

複合条件での集計レポート

複数のコマンドを組み合わせて、詳細なレポートを生成します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# IPアドレスとステータスコードの組み合わせで集計
awk '{print $1, $9}' access.log | sort | uniq -c | sort -rn

# 出力例
#       3 192.168.1.10 200
#       2 192.168.1.20 200
#       1 192.168.1.50 403
#       1 192.168.1.40 404
#       1 192.168.1.30 401
#       1 192.168.1.30 200
#       1 192.168.1.10 200

実践:ワンライナー集計テクニック

日常的に使える便利なワンライナーを紹介します。

ファイル拡張子別のファイル数

1
2
# カレントディレクトリ以下のファイル拡張子を集計
find . -type f -name "*.*" | sed 's/.*\.//' | sort | uniq -c | sort -rn

重複ファイルの検出

1
2
# MD5ハッシュで重複ファイルを検出
find . -type f -exec md5sum {} \; | sort | uniq -w32 -d

トップN件の抽出

1
2
3
4
5
6
7
# 売上金額トップ3の商品
tail -n +2 sales.csv | sort -t',' -k4 -rn | head -3 | cut -d',' -f2,4

# 出力例
# Monitor,28000
# Keyboard,3500
# Keyboard,3500

CSVからTSVへの変換

1
2
3
4
5
# カンマをタブに変換
cat sales.csv | tr ',' '\t'

# または
cut -d',' -f1- --output-delimiter=$'\t' sales.csv

連番付きリストの作成

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# ユニークな商品名に連番を付与
tail -n +2 sales.csv | cut -d',' -f2 | sort -u | nl

# 出力例
#      1 Keyboard
#      2 Monitor
#      3 Mouse
#      4 Notebook
#      5 Pen
#      6 USB Cable

コマンド連携のフロー図

本記事で紹介したコマンドの典型的な連携パターンを図示します。

graph LR
    A[入力データ] --> B[cut<br/>列抽出]
    B --> C[sort<br/>並べ替え]
    C --> D[uniq -c<br/>カウント]
    D --> E[sort -rn<br/>降順ソート]
    E --> F[head<br/>上位抽出]
    F --> G[出力結果]
    
    style A fill:#e3f2fd
    style G fill:#e8f5e9
graph TB
    subgraph 入力
        A1[ファイル1]
        A2[ファイル2]
    end
    
    subgraph 前処理
        B1[sort]
        B2[sort]
    end
    
    subgraph 結合
        C[join]
    end
    
    subgraph 後処理
        D[cut/awk]
    end
    
    A1 --> B1
    A2 --> B2
    B1 --> C
    B2 --> C
    C --> D
    D --> E[出力]
    
    style E fill:#e8f5e9

トラブルシューティング

sortで数値が正しくソートされない

1
2
3
4
5
6
7
# 問題:文字列としてソートされている
echo -e "10\n2\n1" | sort
# 出力: 1, 10, 2(誤り)

# 解決:-n オプションを追加
echo -e "10\n2\n1" | sort -n
# 出力: 1, 2, 10(正しい)

uniqで重複が除去されない

1
2
3
4
5
6
7
# 問題:隣接していない重複が残る
echo -e "a\nb\na" | uniq
# 出力: a, b, a

# 解決:事前にソート
echo -e "a\nb\na" | sort | uniq
# 出力: a, b

cutでフィールドが正しく抽出されない

1
2
3
4
5
6
7
# 問題:区切り文字の指定ミス
echo "a,b,c" | cut -f2
# 出力: a,b,c(タブがないため全体が出力)

# 解決:正しい区切り文字を指定
echo "a,b,c" | cut -d',' -f2
# 出力: b

joinでマッチしない

1
2
3
4
5
# 問題:ソートされていない
# 解決:両ファイルを結合キーでソートしてからjoin

# 問題:フィールド番号の指定ミス
# 解決:-1 と -2 で正しいフィールド番号を指定

まとめ

本記事では、Linuxにおけるデータ集計・分析の基本コマンドを解説しました。

コマンド 主な用途
sort データの並べ替え(数値、辞書順、複数キー)
uniq 重複行の検出・除去・カウント
wc 行数・単語数・バイト数のカウント
cut 特定の列(フィールド)の抽出
paste 複数ファイルの行を横に結合
join 共通キーでの2ファイル結合

これらのコマンドをパイプで組み合わせることで、以下のような処理が可能になります。

  • アクセスログのIPアドレス集計
  • CSVデータの特定列の抽出と集計
  • 重複データの検出とクレンジング
  • 複数ファイルのデータ結合

grep、sed、awkと組み合わせることで、さらに高度なテキスト処理が可能です。関連記事も参照し、コマンドラインでのデータ処理スキルを磨いてください。

参考リンク