「設定ファイルの値を一括で書き換えたい」「CSV から必要な列だけ抜き出して合計を出したい」「ログから特定パターンの行を除去したい」
こういった テキスト加工 の場面で登場するのが sed と awk だ。どちらもパイプラインで活躍するフィルタコマンドだが、得意分野がはっきり違う。
この記事では sed と awk の基本から実践パターン、さらに両者を組み合わせたワークフローまで解説する。
sedとawk:何が違うのか
一言で整理するとこうなる。
| sed | awk | |
|---|---|---|
| 正式名称 | Stream Editor | パターンスキャンと処理言語 |
| 得意なこと | 行単位のテキスト変換(置換・削除・挿入) | 列指向のデータ処理(抽出・計算・集計) |
| 考え方 | 「テキストを書き換える」 | 「テキストからデータを取り出す」 |
| 典型的な用途 | 設定ファイルの値変更、ログの整形 | CSV/TSV の集計、アクセスログ解析 |
どちらもパイプラインの中で | を挟んで使うフィルタコマンドという点では同じだ。sed は 変換 に強く、awk は 抽出と計算 に強い。この使い分けを頭に入れておくと、どちらを使うべきか迷わなくなる。
sed の基本操作
sed は「ストリームエディタ」の名のとおり、テキストを1行ずつ読んで変換し、結果を出力するコマンドだ。
置換
最も使う操作が s(substitute)コマンドによる置換だ。
# 各行で最初にマッチしたものだけ置換
sed 's/old/new/' file.txt
# 各行のすべてのマッチを置換(g フラグ)
sed 's/old/new/g' file.txt
# ファイルを直接編集(-i オプション)
sed -i 's/old/new/g' file.txt
行指定と範囲指定
# 3行目だけ置換
sed '3s/old/new/' file.txt
# 2行目から5行目を置換
sed '2,5s/old/new/g' file.txt
# 最終行だけ置換
sed '$s/old/new/' file.txt
行の削除・挿入・出力
# パターンにマッチする行を削除
sed '/pattern/d' file.txt
# パターンにマッチした行の後に挿入
sed '/pattern/a\new line' file.txt
# パターンにマッチした行の前に挿入
sed '/pattern/i\new line' file.txt
# 特定範囲の行だけ出力(-n と p の組み合わせ)
sed -n '10,20p' file.txt
複数操作と区切り文字
# 複数の置換を一度に実行
sed -e 's/foo/bar/g' -e 's/baz/qux/g' file.txt
# 区切り文字を変更(パスやURLの操作で便利)
sed 's|/usr/local|/opt|g' config.txt
区切り文字は / 以外にも | # @ などが使える。パスを扱うときに / をエスケープしなくて済むので覚えておくと便利だ。
sed 実践パターン
設定ファイルの一括書き換え
サーバー移行やデプロイ時に設定値を一括で変更するケースだ。
# .env ファイルの DB_HOST を書き換え
NEW_HOST="db-prod.example.com"
sed -i "s/DB_HOST=.*/DB_HOST=${NEW_HOST}/" .env
# 特定行をコメントアウト
sed -i 's/^PermitRootLogin yes/# PermitRootLogin yes/' /etc/ssh/sshd_config
# コメントを解除
sed -i 's/^# *PermitRootLogin/PermitRootLogin/' /etc/ssh/sshd_config
ログファイルのクリーニング
# 空行を除去
sed '/^$/d' file.txt
# 先頭と末尾の空白を除去
sed 's/^[[:space:]]*//;s/[[:space:]]*$//' file.txt
# ANSIエスケープシーケンス(色コード)を除去
sed 's/\x1b\[[0-9;]*m//g' colored-output.txt
バッチ処理(find との組み合わせ)
# プロジェクト内の全 .txt ファイルで年号を一括置換
find . -name "*.txt" -exec sed -i 's/2025/2026/g' {} +
# .env.example から .env を生成しつつ値を置換
sed 's/DB_PASSWORD=changeme/DB_PASSWORD=s3cur3P@ss/' .env.example > .env
awk の基本操作
awk はテキストを フィールド(列)単位で処理する言語だ。各行を自動的に空白で分割し、$1、$2 などでアクセスできる。
なお、gawk 5.4.0 ではデフォルトの正規表現エンジンが MinRX に変更された。完全 POSIX 準拠になったため、従来の GNU 拡張に依存していたパターンは動作が変わる可能性がある。従来のエンジンを使いたい場合は環境変数 GAWK_GNU_MATCHERS=1 を設定する。
列の抽出
# 1列目と3列目を出力
awk '{print $1, $3}' data.tsv
# 区切り文字を指定(CSV の場合)
awk -F',' '{print $1, $2}' data.csv
# 行全体を出力($0 は行全体)
awk '{print $0}' file.txt
パターンマッチと条件
# "error" を含む行だけ出力
awk '/error/ {print $0}' logfile.txt
# 3列目が100より大きい行を出力
awk '$3 > 100 {print $1, $3}' data.txt
# 複数条件の組み合わせ
awk '$3 > 100 && $2 == "active" {print $1, $3}' data.txt
組み込み変数
awk には便利な組み込み変数がある。
| 変数 | 意味 |
|---|---|
NR | 現在の行番号(Number of Records) |
NF | 現在の行のフィールド数(Number of Fields) |
FS | フィールド区切り文字(Field Separator) |
OFS | 出力時のフィールド区切り文字 |
# 行番号付きで出力
awk '{print NR": "$0}' file.txt
# 各行のフィールド数を表示
awk '{print NR": "NF" fields"}' data.txt
# 最終フィールドを出力
awk '{print $NF}' data.txt
BEGIN / END ブロックと集計
# ヘッダーとフッターを付ける
awk 'BEGIN {print "Name,Score"} {print $1","$3} END {print "---done---"}' data.txt
# 2列目を合計
awk '{sum += $2} END {print "Total:", sum}' sales.txt
# 平均を計算
awk '{sum += $2; count++} END {print "Average:", sum/count}' sales.txt
printf による出力整形
# 左寄せ20文字、右寄せ10文字(小数点2桁)
awk '{printf "%-20s %10.2f\n", $1, $3}' data.txt
printf は C 言語と同じフォーマット指定子が使える。テーブル形式の出力を作るときに重宝する。
awk 実践パターン
CSV/TSV データの集計
# カテゴリ別の売上合計(連想配列を使用)
# 入力: category,product,amount の CSV
awk -F',' '{
sales[$1] += $3
}
END {
for (cat in sales)
printf "%-15s %10.0f\n", cat, sales[cat]
}' sales.csv
# 最大値・最小値・平均を一度に計算
awk 'BEGIN {max = -999999; min = 999999}
{
sum += $2
count++
if ($2 > max) max = $2
if ($2 < min) min = $2
}
END {
printf "Max: %.2f Min: %.2f Avg: %.2f\n", max, min, sum/count
}' data.txt
アクセスログ解析
# IPアドレス別のアクセス数(上位10件)
awk '{count[$1]++} END {for (ip in count) print count[ip], ip}' access.log | sort -rn | head -10
# HTTPステータスコード別の集計
# CLF形式: IP - - [date] "method path proto" status size
awk '{print $9}' access.log | sort | uniq -c | sort -rn
出力フォーマット整形
# テーブル形式でユーザー情報を表示
awk -F':' 'BEGIN {
printf "%-20s %-6s %-6s %s\n", "USER", "UID", "GID", "HOME"
printf "%-20s %-6s %-6s %s\n", "----", "---", "---", "----"
}
$3 >= 1000 && $3 < 65534 {
printf "%-20s %-6s %-6s %s\n", $1, $3, $4, $6
}' /etc/passwd
sed + awk の組み合わせ
sed と awk はパイプラインで繋ぐことで真価を発揮する。sed で前処理(整形・フィルタ)→ awk で集計という流れだ。
# CSV のヘッダー行を除去してから3列目を集計
sed '1d' sales.csv | awk -F',' '{sum += $3} END {print "Total:", sum}'
# ログから ERROR 行を抽出し、時刻とメッセージだけ表示
sed -n '/ERROR/p' app.log | awk '{print $1, $2, substr($0, index($0,$5))}'
# /etc/passwd からシェルが bash のユーザーだけ抽出し整形
grep '/bash$' /etc/passwd | awk -F':' '{printf "%-15s UID=%-6s %s\n", $1, $3, $6}'
# syslog から今日のエラーを集計
sed -n "/$(date '+%b %e')/p" /var/log/syslog | awk '/error|fail/ {count[$5]++} END {for (s in count) print count[s], s}' | sort -rn
モダン代替:sd
sd は Rust 製の sed 代替ツールだ。sed の s/old/new/g という構文に比べて、エスケープが少なくシンプルに書ける。
インストール
# WSL 環境なら Linux と同じ
cargo install sd
基本的な使い方
# 標準入力から置換
echo "hello world" | sd 'world' 'earth'
# ファイルを直接編集
sd 'old' 'new' file.txt
sed との比較
| 操作 | sed | sd |
|---|---|---|
| 基本置換 | sed 's/foo/bar/g' | sd 'foo' 'bar' |
| パス置換 | sed 's|/usr/local|/opt|g' | sd '/usr/local' '/opt' |
| 正規表現グループ | sed 's/\(foo\)/[\1]/g' | sd '(foo)' '[$1]' |
| ファイル編集 | sed -i 's/foo/bar/g' file | sd 'foo' 'bar' file |
sd は正規表現にデフォルトで PCRE 風の構文を使うため、\( \) のようなエスケープが不要だ。パスの置換でも区切り文字を変更する必要がない。
# 複数行マッチ(v1.1.0 以降)
sd --across 'start\n.*\nend' 'replaced' file.txt
sed に慣れているなら無理に乗り換える必要はない。ただ、正規表現のエスケープが複雑になりがちなワンライナーでは sd のほうがミスが減る。使い分けの目安としては、シンプルな置換は sd、行指定や複雑なスクリプトは sed というのがバランスが良い。
まとめ
sed と awk はテキスト加工における定番の組み合わせだ。
- sed — 行単位の置換・削除・挿入。設定ファイルの書き換えやログのクリーニングに最適
- awk — 列単位の抽出・計算・集計。CSV/TSV の処理やアクセスログ解析に最適
- sed + awk — パイプラインで繋いで前処理→集計。sed で整形、awk で計算が基本パターン
- sd — sed の構文が面倒なときのモダン代替。エスケープが少なくシンプル
どちらも50年近い歴史を持つ枯れたツールで、Linux サーバーなら必ずインストールされている。まずは sed の s コマンドと awk の {print $1} から始めて、徐々にパターンを増やしていくのがおすすめだ。
テキスト検索には grep・ripgrep、JSON の加工には jq も合わせて使えると、CLI でのデータ処理がさらに快適になる。コマンド全体の見取り図は CLIツール完全マップ を参照してほしい。