はじめに

Linuxにおいて、テキストデータの処理は日常的なタスクです。ログファイルの解析、設定ファイルの編集、CSVデータの加工など、テキストを効率的に操作できるかどうかが作業効率を大きく左右します。

本記事では、Linuxテキスト処理の「三種の神器」と呼ばれるgrep、sed、awkを体系的に解説します。これらのコマンドを組み合わせることで、複雑なテキスト処理も数行のコマンドで実現できるようになります。

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

動作確認環境

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

項目 内容
OS Ubuntu 24.04 LTS
シェル bash 5.3
grep GNU grep 3.11
sed GNU sed 4.9
awk GNU awk (gawk) 5.2.1

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

grep - パターンマッチングの王者

grepは「Global Regular Expression Print」の略で、テキストから正規表現にマッチする行を抽出するコマンドです。ログ解析やコード検索など、あらゆる場面で活躍します。

基本的な使い方

1
2
3
4
5
6
7
8
# ファイル内で "error" を含む行を検索
grep "error" /var/log/syslog

# 複数ファイルを検索
grep "TODO" *.py

# 再帰的にディレクトリを検索
grep -r "function" src/

主要オプション

grepには多くのオプションがあります。特に重要なものを紹介します。

オプション 説明
-i 大文字・小文字を区別しない
-v パターンにマッチしない行を表示
-n 行番号を表示
-c マッチした行数をカウント
-l マッチしたファイル名のみ表示
-L マッチしなかったファイル名を表示
-w 単語単位でマッチ
-x 行全体でマッチ
-A n マッチ行の後n行を表示
-B n マッチ行の前n行を表示
-C n マッチ行の前後n行を表示
-r ディレクトリを再帰的に検索
-E 拡張正規表現を使用
-P Perl互換正規表現を使用
-o マッチした部分のみ表示
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 大文字小文字を無視して検索
grep -i "warning" app.log

# エラーを含まない行を抽出
grep -v "error" access.log

# 行番号付きで検索結果を表示
grep -n "def " script.py

# マッチ行と前後2行を表示(コンテキスト確認)
grep -C 2 "Exception" error.log

# 単語として "test" にマッチ("testing" などは除外)
grep -w "test" README.md

正規表現オプションの詳細

grepは3種類の正規表現をサポートしています。

graph TD
    A[grep 正規表現モード] --> B[BRE<br/>基本正規表現<br/>デフォルト]
    A --> C[ERE<br/>拡張正規表現<br/>-E オプション]
    A --> D[PCRE<br/>Perl互換正規表現<br/>-P オプション]
    
    B --> B1["\\+, \\?, \\|, \\(\\)<br/>エスケープ必要"]
    C --> C1["+, ?, |, ()<br/>そのまま使用]
    D --> D1["\\d, \\w, 先読み<br/>高度な機能"]
    
    style B fill:#e3f2fd
    style C fill:#e8f5e9
    style D fill:#fff3e0

基本正規表現(BRE)

デフォルトのモードです。メタ文字の一部はエスケープが必要です。

1
2
3
4
5
6
# 基本正規表現での例
# 1回以上の繰り返しには \+ を使用
grep 'a\+' file.txt

# グループ化には \( \) を使用
grep '\(ab\)\{2\}' file.txt   # abab にマッチ

拡張正規表現(ERE)

-Eオプション(またはegrepコマンド)で有効になります。より直感的な記法が使えます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 拡張正規表現(-E オプション)
# OR検索
grep -E 'error|warning|fatal' syslog

# 繰り返し(1回以上)
grep -E 'a+' file.txt

# 0回または1回
grep -E 'https?' urls.txt    # http または https

# グループ化
grep -E '(ab){2,3}' file.txt  # abab または ababab

# 数字の範囲
grep -E '[0-9]{3}-[0-9]{4}' data.txt  # 電話番号パターン

Perl互換正規表現(PCRE)

-Pオプションで有効になります。より高度なパターンマッチングが可能です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Perl互換正規表現(-P オプション)
# \d(数字)、\w(単語文字)、\s(空白)が使用可能
grep -P '\d{3}-\d{4}' data.txt

# 先読み(lookahead)
grep -P 'error(?=.*critical)' log.txt  # error の後に critical がある行

# 後読み(lookbehind)
grep -P '(?<=user:)\w+' auth.log  # user: の後の単語

# 非貪欲マッチ
grep -Po '".*?"' data.json  # 最短マッチで引用符内を抽出

正規表現メタ文字一覧

メタ文字 意味
. 任意の1文字 a.c → abc, adc
^ 行頭 ^Error → 行頭のError
$ 行末 end$ → 行末のend
* 直前の0回以上の繰り返し ab* → a, ab, abb
+ 直前の1回以上の繰り返し(ERE) ab+ → ab, abb
? 直前の0回または1回(ERE) ab? → a, ab
[] 文字クラス [aeiou] → 母音
[^] 否定文字クラス [^0-9] → 数字以外
\b 単語境界 \bword\b → 単語
\< 単語の先頭 \<pre
\> 単語の末尾 fix\>

POSIXブラケット表現

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 英数字にマッチ
grep '[[:alnum:]]' file.txt

# 空白文字にマッチ
grep '[[:space:]]' file.txt

# 主なクラス
# [:alpha:]  アルファベット
# [:digit:]  数字
# [:alnum:]  英数字
# [:space:]  空白文字
# [:upper:]  大文字
# [:lower:]  小文字
# [:punct:]  句読点

grepの実践的な活用例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# IPアドレスの抽出
grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' access.log

# メールアドレスの抽出
grep -Eo '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}' contacts.txt

# 特定の時間帯のログを抽出
grep -E '^2026-01-07 1[0-5]:' application.log

# コメント行と空行を除外
grep -vE '^\s*(#|$)' config.conf

# 複数条件のAND検索(パイプで連結)
grep 'ERROR' app.log | grep 'database'

# 複数条件のOR検索
grep -E 'ERROR|WARNING|FATAL' app.log

# 特定の拡張子を除外して検索
grep -r --exclude='*.log' 'password' .

sed - ストリームエディタ

sedは「Stream Editor」の略で、入力ストリームに対してテキスト変換を行うコマンドです。置換、削除、挿入など、強力な編集機能を提供します。

基本構文

1
sed [オプション] 'アドレス コマンド' ファイル

アドレスは操作対象の行を指定し、コマンドは実行する操作を指定します。

主要オプション

オプション 説明
-n パターンスペースの自動出力を抑制
-i ファイルを直接編集(インプレース)
-e 複数のコマンドを指定
-E / -r 拡張正規表現を使用
-f スクリプトファイルからコマンドを読み込み

置換コマンド(s)

最も頻繁に使用されるコマンドです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 基本的な置換(各行の最初のマッチのみ)
sed 's/old/new/' file.txt

# グローバル置換(行内のすべてのマッチ)
sed 's/old/new/g' file.txt

# 大文字小文字を無視
sed 's/error/ERROR/gi' log.txt

# 2番目以降のマッチを置換
sed 's/the/THE/2' file.txt

# n番目のマッチのみ置換
sed 's/the/THE/3' file.txt

# 区切り文字を変更(パスなどに便利)
sed 's|/usr/local|/opt|g' config.txt
sed 's#http://#https://#g' urls.txt
graph LR
    A[入力行] --> B[パターンマッチ]
    B --> C{マッチ?}
    C -->|Yes| D[置換実行]
    C -->|No| E[変更なし]
    D --> F[出力]
    E --> F
    
    style B fill:#e3f2fd
    style D fill:#e8f5e9

後方参照

キャプチャグループを使って、マッチした内容を参照できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 単語の順序を入れ替え
echo "hello world" | sed 's/\(\w\+\) \(\w\+\)/\2 \1/'
# 出力: world hello

# 拡張正規表現版(より読みやすい)
echo "hello world" | sed -E 's/(\w+) (\w+)/\2 \1/'

# 日付フォーマットの変換(YYYY-MM-DD → DD/MM/YYYY)
echo "2026-01-07" | sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3\/\2\/\1/'
# 出力: 07/01/2026

# HTMLタグの抽出
echo '<a href="https://example.com">Link</a>' | sed -E 's/.*href="([^"]+)".*/\1/'
# 出力: https://example.com

アドレス指定

特定の行やパターンにマッチする行のみを操作できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# 行番号で指定
sed '3s/old/new/' file.txt        # 3行目のみ置換
sed '1,5s/old/new/' file.txt      # 1-5行目を置換
sed '10,$s/old/new/' file.txt     # 10行目から最後まで

# パターンで指定
sed '/error/s/foo/bar/' file.txt  # error を含む行のみ置換
sed '/^#/d' config.conf           # コメント行を削除

# 範囲指定
sed '/START/,/END/d' file.txt     # START から END までの行を削除
sed '1,/^$/d' file.txt            # 最初の空行までを削除

# 否定(!)
sed '/important/!d' file.txt      # important を含まない行を削除
sed '1,10!d' file.txt             # 1-10行目以外を削除(=1-10行目を残す)

削除コマンド(d)

1
2
3
4
5
6
7
8
9
# 特定の行を削除
sed '5d' file.txt                 # 5行目を削除
sed '1,10d' file.txt              # 1-10行目を削除
sed '$d' file.txt                 # 最終行を削除

# パターンにマッチする行を削除
sed '/DEBUG/d' app.log            # DEBUG を含む行を削除
sed '/^$/d' file.txt              # 空行を削除
sed '/^[[:space:]]*$/d' file.txt  # 空白のみの行も削除

挿入・追加・変更コマンド

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 挿入(i): 指定行の前に追加
sed '1i\# This is a header' file.txt

# 追加(a): 指定行の後に追加
sed '$a\# End of file' file.txt

# 変更(c): 行全体を置き換え
sed '/deprecated/c\# This line was removed' code.py

# 複数行の挿入(バックスラッシュで改行)
sed '1i\
Line 1\
Line 2\
Line 3' file.txt

出力コマンド(p)

1
2
3
4
5
6
7
8
# 特定の行のみ表示
sed -n '10p' file.txt             # 10行目のみ
sed -n '5,10p' file.txt           # 5-10行目
sed -n '/pattern/p' file.txt      # パターンにマッチする行
sed -n '1~2p' file.txt            # 奇数行のみ(GNU拡張)

# 置換が成功した行のみ表示
sed -n 's/error/ERROR/p' log.txt

sedの実践的な活用例

 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
# 設定ファイルの一括編集
sed -i 's/localhost/192.168.1.100/g' config.yml

# バックアップを作成してから編集
sed -i.bak 's/DEBUG=true/DEBUG=false/' settings.conf

# 複数の置換を同時実行
sed -e 's/foo/bar/g' -e 's/baz/qux/g' file.txt

# ファイル間で設定値を移行
new_value=$(grep 'API_KEY' new.env | cut -d= -f2)
sed -i "s/API_KEY=.*/API_KEY=$new_value/" old.env

# ログから特定のフィールドを抽出
sed -E 's/.*\[([0-9]{4}-[0-9]{2}-[0-9]{2})\].*/\1/' access.log

# 先頭・末尾の空白を削除(トリム)
sed 's/^[[:space:]]*//; s/[[:space:]]*$//' file.txt

# 連続する空行を1行にまとめる
sed '/^$/N;/^\n$/d' file.txt

# CRLFをLFに変換
sed 's/\r$//' windows.txt > unix.txt

# 特定範囲の行を別コマンドで処理
sed '/^---$/,/^---$/{ s/title:/TITLE:/; }' markdown.md

awk - テキスト処理言語

awkはフィールド指向のテキスト処理言語です。データをレコード(行)とフィールド(列)として扱い、強力なパターンマッチングとプログラミング機能を提供します。

基本構文

1
awk 'パターン { アクション }' ファイル

パターンがマッチした行に対してアクションが実行されます。パターンを省略するとすべての行が対象になります。

graph TD
    A[入力レコード<br/>各行] --> B[パターン評価]
    B --> C{マッチ?}
    C -->|Yes| D[アクション実行]
    C -->|No| E[次のレコードへ]
    D --> E
    E --> F{次の行?}
    F -->|Yes| A
    F -->|No| G[終了]
    
    style B fill:#e3f2fd
    style D fill:#e8f5e9

フィールド変数

awkは入力を自動的にフィールドに分割します。

変数 意味
$0 レコード全体(行全体)
$1, $2, … 各フィールド
NF フィールド数
NR 現在のレコード番号(行番号)
FNR 現在のファイル内でのレコード番号
FS フィールド区切り文字(デフォルト: 空白)
OFS 出力フィールド区切り文字
RS レコード区切り文字(デフォルト: 改行)
ORS 出力レコード区切り文字
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 基本的なフィールド抽出
echo "apple banana cherry" | awk '{print $2}'
# 出力: banana

# 複数フィールドを出力
awk '{print $1, $3}' data.txt

# 最後のフィールド
awk '{print $NF}' data.txt

# 最後から2番目のフィールド
awk '{print $(NF-1)}' data.txt

# フィールド数と行番号
awk '{print NR, NF, $0}' data.txt

フィールド区切り文字の指定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# コロン区切り(/etc/passwd など)
awk -F: '{print $1, $7}' /etc/passwd

# カンマ区切り(CSV)
awk -F, '{print $2}' data.csv

# 複数の区切り文字(正規表現)
awk -F'[,;:]' '{print $1, $2}' mixed.txt

# タブ区切り
awk -F'\t' '{print $1}' data.tsv

# 出力区切り文字の指定
awk -F: 'BEGIN{OFS=","} {print $1, $3, $7}' /etc/passwd

BEGINとENDブロック

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# BEGIN: 処理開始前に1度だけ実行
# END: 処理終了後に1度だけ実行

awk 'BEGIN {print "=== Report ==="} 
     {print NR, $0} 
     END {print "Total lines:", NR}' file.txt

# 合計値の計算
awk 'BEGIN {sum=0} {sum+=$1} END {print "Sum:", sum}' numbers.txt

# 平均値の計算
awk '{sum+=$1; count++} END {print "Average:", sum/count}' numbers.txt

パターンマッチング

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 正規表現パターン
awk '/error/' log.txt              # error を含む行
awk '/^#/' config.conf             # コメント行
awk '!/^#/' config.conf            # コメント行以外

# フィールドでのマッチング
awk '$3 > 100' data.txt            # 3番目のフィールドが100より大きい行
awk '$1 == "admin"' users.txt      # 1番目のフィールドが "admin" の行
awk '$2 ~ /^[0-9]+$/' data.txt     # 2番目のフィールドが数字のみの行

# 複合条件
awk '$3 > 100 && $4 == "active"' data.txt
awk '$1 == "error" || $1 == "fatal"' log.txt

# 範囲パターン
awk '/START/,/END/' file.txt       # START から END までの行
awk 'NR==5,NR==10' file.txt        # 5行目から10行目まで

組み込み関数

awkには多くの組み込み関数があります。

文字列関数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# length - 文字列長
awk '{print length($1)}' file.txt

# substr - 部分文字列
awk '{print substr($1, 1, 3)}' file.txt  # 最初の3文字

# index - 位置検索
awk '{print index($0, "error")}' log.txt

# split - 文字列分割
awk '{n=split($1, arr, "-"); print arr[1], arr[2]}' dates.txt

# sub/gsub - 置換
awk '{sub(/old/, "new"); print}' file.txt      # 最初のマッチを置換
awk '{gsub(/old/, "new"); print}' file.txt     # すべてのマッチを置換

# tolower/toupper - 大文字小文字変換
awk '{print toupper($1)}' file.txt
awk '{print tolower($0)}' file.txt

# sprintf - フォーマット
awk '{printf "%-10s %5d\n", $1, $2}' data.txt

数値関数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# int - 整数変換
awk '{print int($1)}' decimals.txt

# sqrt - 平方根
awk '{print sqrt($1)}' numbers.txt

# rand/srand - 乱数
awk 'BEGIN {srand(); print rand()}'

# sin, cos, log, exp なども使用可能

条件分岐とループ

awkは完全なプログラミング言語です。

 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
# if-else
awk '{
    if ($3 > 100) 
        print $1, "high"
    else if ($3 > 50)
        print $1, "medium"
    else
        print $1, "low"
}' data.txt

# for ループ
awk '{
    for (i=1; i<=NF; i++) 
        print $i
}' file.txt

# while ループ
awk '{
    i = 1
    while (i <= NF) {
        print $i
        i++
    }
}' file.txt

# 配列を使った集計
awk '{count[$1]++} END {for (key in count) print key, count[key]}' log.txt

連想配列

awkの強力な機能の一つです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# 単語の出現回数をカウント
awk '{
    for (i=1; i<=NF; i++) 
        words[$i]++
} 
END {
    for (w in words) 
        print w, words[w]
}' text.txt

# グループごとの合計
awk -F, '{sum[$1] += $2} 
END {
    for (category in sum) 
        print category, sum[category]
}' sales.csv

# ユニークな値の抽出
awk '!seen[$1]++' data.txt

awkの実践的な活用例

 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
# アクセスログからIPアドレスごとのアクセス数を集計
awk '{ip[$1]++} END {for (i in ip) print i, ip[i]}' access.log | sort -t' ' -k2 -rn | head -10

# CSVの特定列を集計
awk -F, 'NR>1 {sum+=$3} END {print "Total:", sum}' sales.csv

# プロセスのメモリ使用量を集計
ps aux | awk 'NR>1 {sum+=$6} END {print "Total Memory:", sum/1024, "MB"}'

# 特定フィールドでフィルタリングしてフォーマット
awk -F: '$3 >= 1000 {printf "User: %-15s UID: %d\n", $1, $3}' /etc/passwd

# JSONからフィールド抽出(簡易版)
awk -F'"' '/"name"/ {print $4}' data.json

# ディスク使用率が高いファイルシステムを警告
df -h | awk 'NR>1 {gsub(/%/,"",$5); if ($5 > 80) print "Warning:", $6, $5"%"}'

# Apacheログから時間帯別アクセス数
awk -F'[: ]' '{hour[$5]++} END {for (h in hour) print h":00", hour[h]}' access.log | sort

# 重複行の検出
awk 'seen[$0]++ == 1' file.txt

# 列の入れ替え
awk '{print $3, $1, $2}' data.txt

# 複数ファイルの処理
awk 'FNR==1 {print "=== " FILENAME " ==="} {print}' *.log

三種の神器を組み合わせる

grep、sed、awkを組み合わせることで、より複雑なテキスト処理が実現できます。

graph LR
    A[入力データ] --> B[grep<br/>フィルタリング]
    B --> C[sed<br/>変換・整形]
    C --> D[awk<br/>集計・分析]
    D --> E[出力]
    
    style B fill:#e3f2fd
    style C fill:#e8f5e9
    style D fill:#fff3e0

実践的なパイプライン例

 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
# エラーログから日付とメッセージを抽出してフォーマット
grep 'ERROR' app.log | \
sed -E 's/.*\[([0-9-]+)\].*ERROR: (.*)/\1 | \2/' | \
awk '{print NR": "$0}'

# アクセスログから404エラーのURLを集計
grep ' 404 ' access.log | \
awk '{print $7}' | \
sort | uniq -c | \
sort -rn | head -10

# 設定ファイルから有効な設定のみ抽出
grep -vE '^\s*(#|$)' config.conf | \
sed 's/[[:space:]]*=[[:space:]]*/=/' | \
awk -F= '{print $1 " -> " $2}'

# ログから特定期間のデータを抽出して集計
sed -n '/2026-01-01/,/2026-01-07/p' access.log | \
grep 'POST' | \
awk '{sum+=$10} END {print "Total bytes:", sum}'

# CSV変換パイプライン
cat data.tsv | \
sed 's/\t/,/g' | \
awk -F, 'NR>1 && $3!="" {print}' > filtered.csv

ログ解析の総合例

実際のWebサーバーログを解析する例を示します。

 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
# アクセスログのサンプル形式
# 192.168.1.1 - - [07/Jan/2026:10:00:00 +0900] "GET /api/users HTTP/1.1" 200 1234

# 1. ステータスコードごとの集計
awk '{print $9}' access.log | sort | uniq -c | sort -rn

# 2. 時間帯別アクセス数
awk -F'[:[]' '{print $2}' access.log | \
cut -d: -f1 | \
sort | uniq -c

# 3. レスポンスタイムの遅いリクエストを抽出
awk '$NF > 1.0 {print $7, $NF"s"}' access.log | \
sort -t' ' -k2 -rn | head -20

# 4. エラー率の計算
awk '{
    total++
    if ($9 >= 400) errors++
} 
END {
    print "Error rate:", errors/total*100 "%"
}' access.log

# 5. 特定IPからの不審なアクセスを検出
grep -E '(sql|union|select|drop)' access.log | \
awk '{print $1}' | sort | uniq -c | sort -rn

まとめ

本記事では、Linuxテキスト処理の三種の神器であるgrep、sed、awkの基本から実践的な活用方法まで解説しました。

これらのツールは、それぞれ以下のような場面で力を発揮します。

ツール 主な用途
grep パターンマッチングによる行の抽出・検索
sed ストリーム編集による置換・削除・挿入
awk フィールド処理、条件分岐、集計

これらを組み合わせることで、複雑なログ解析やデータ変換も効率的に実行できます。日常的な作業で少しずつ使い始め、徐々にパターンを増やしていくことで、自然と身についていきます。

参考リンク