テキスト処理コマンド完全ガイド — 50+ コマンドを 7 カテゴリで

現場のテキスト処理は 「ログから問題を抽出する」「CSV を集計する」「設定ファイルを一括書き換える」 の 3 つに大別されます。これを覚えると Excel を開かずにターミナルだけで多くの仕事が片付きます。 AWS 案件でも CloudWatch Logs を aws logs filter-log-events で取ってから先は結局 grep/awk のお仕事です。

本記事は grep / sed / awk の三大テキスト処理ツールを軸に、補助的に使う 50+ コマンドを 実例パターン付きで解説します。最初は 各セクションの「典型パターン」だけ覚えて、必要時に詳細を引く 使い方を想定。

動作環境: 本稿は GNU 系(Linux 標準)を前提。macOS の BSD sed-i の挙動が違うので、 クロスプラットフォームスクリプトでは sed -i.bak '...' のように拡張子を必ず指定するか、perl -i -pe を使うのが安全です。

目次

  1. grep — テキスト検索の王道
  2. sed — ストリームエディタ(置換・抽出)
  3. awk — フィールド単位の処理・集計
  4. 列・行操作 (cut / paste / join / column)
  5. 並べ替え・重複 (sort / uniq / shuf)
  6. 変換・カウント (tr / wc / nl / rev)
  7. 差分・パッチ (diff / comm / patch)
  8. よく使う組み合わせパターン
  9. 用語集

1. grep — テキスト検索の王道

テキスト処理の基礎。ファイル内/標準入力から正規表現にマッチする行を抽出します。 現場で grep が使えないと話にならないレベル。

基本オプション

オプション意味
-i大小無視
-vマッチしない行を出力(除外)
-n行番号を表示
-cマッチ行数だけ表示
-lマッチしたファイル名のみ
-Lマッチしないファイル名のみ
-r / -R再帰検索(ディレクトリ対象)
-w単語境界マッチ
-x行全体マッチ
-A N / -B N / -C Nマッチ後 / 前 / 前後 N 行も表示
-E拡張正規表現 (egrep 相当)
-PPerl 互換正規表現 (PCRE)
-F固定文字列として扱う (fgrep 相当、メタ文字無効)
-oマッチ部分のみ抽出
-hファイル名を出力しない(複数ファイル時)
--include / --exclude再帰時のファイル絞り込み
--exclude-dir再帰時にディレクトリ除外

実例

# ログから ERROR を抽出(行番号付き)
grep -n ERROR app.log

# 大小無視 + 単語境界マッチで warning を厳密検索
grep -iw warning app.log

# 除外して条件絞り込み( NOISY な行を除く)
grep ERROR app.log | grep -v 'connection reset'

# マッチ前後 3 行(コンテキスト確認に頻出)
grep -C 3 'OutOfMemory' app.log

# プロジェクト全体から TODO を再帰検索(.git/.terraform を除外)
grep -rn 'TODO' --exclude-dir={.git,.terraform,node_modules} .

# ファイル名拡張子で絞る
grep -rn 'aws_s3_bucket' --include='*.tf' .

# マッチした行を含むファイル名のみ
grep -rl 'old_function_name' src/

# 抽出のみ(マッチした文字列だけ)
grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' access.log | sort -u

# Perl 正規表現(先読みなどが使える)
grep -P '(?<=user=)\w+' /var/log/auth.log

# 複数パターンの OR 検索
grep -E 'error|warning|critical' app.log
grep -e error -e warning -e critical app.log    # -e で複数指定も可

# パターンファイルから読み込み(大量パターン時)
grep -f patterns.txt huge.log

# 圧縮ログを直接 grep
zgrep ERROR /var/log/syslog.1.gz
grep -P (PCRE) は Linux でしか使えないことが多い(macOS の BSD grep は非対応)。 クロスプラットフォーム性を意識するなら -E 止まりにするか、Perl/Python に逃げる。

ripgrep (rg) — モダンな代替

grep の上位互換。デフォルトで .gitignore 尊重・並列実行・Unicode 対応・色付き。 インストールしたらほぼ grep -rn の代わりに使えます。

rg ERROR                   # カレントを再帰検索(デフォルトで隠しと .gitignore を除外)
rg -t tf 'aws_s3_bucket'   # 特定タイプ (terraform) のみ
rg -i --hidden 'TODO'      # 大小無視 + 隠しファイル含む
rg --files | wc -l         # 検索対象ファイル数

2. sed — ストリームエディタ(置換・抽出)

テキストを 1 行ずつ 読んで処理する。最も使う機能は s/old/new/ 置換。 awk より「構造を意識せず一括書き換え」に強い。

基本構文

sed [オプション] 'コマンド' ファイル。コマンドの主要なものは:

コマンド意味
s/pat/rep/1 行内の最初の match を置換
s/pat/rep/g1 行内の全 match を置換 (global)
s/pat/rep/I大小無視で置換
NpN 行目を表示 (例: 5p)
NdN 行目を削除
/pat/dマッチする行を削除
/pat/pマッチする行を表示 (要 -n)
N,MpN〜M 行を表示
1i text1 行目の前に挿入
$a text最終行の後に追加
y/abc/ABC/文字を 1:1 変換 (tr 相当)
オプション意味
-nデフォルト出力を抑制 (p との組合わせで明示出力)
-iファイル直接編集(GNU は -i 単独 OK、BSD は -i ''
-i.bak編集前にバックアップ作成(クロスプラットフォーム安全)
-E拡張正規表現
-e複数コマンド指定
-f script.sedスクリプトファイル読み込み

実例 — 置換

# 標準的な置換 (確認のためまず -e で出力)
sed 's/foo/bar/g' file.txt

# ファイル直接編集(GNU 限定)
sed -i 's/foo/bar/g' file.txt

# クロスプラットフォーム安全(バックアップ作成)
sed -i.bak 's/foo/bar/g' file.txt

# 複数ファイル一括置換
sed -i 's/old_endpoint/new_endpoint/g' src/*.tf

# 区切り文字を / 以外に(パスを置換する時に便利)
sed -i 's|/old/path|/new/path|g' config.yaml

# 大小無視
sed -i 's/error/ERROR/Ig' app.log

# 拡張正規表現で複数パターン
sed -E -i 's/(GET|POST) /[\1] /g' access.log

実例 — 抽出・削除

# 5 行目だけ表示
sed -n '5p' file

# 10〜20 行を抽出
sed -n '10,20p' file

# パターンを含む行を削除
sed '/^#/d' config.yaml          # コメント行を削除
sed -i '/^$/d' file              # 空行を削除(直接編集)

# パターンに一致する行だけ抽出
sed -n '/ERROR/p' app.log

# パターン後の行を抽出
sed -n '/START/,/END/p' file     # START から END までのブロック

# 1 行目を削除(CSV のヘッダ取り除き)
sed '1d' data.csv

# 末尾 5 行を削除
sed -i '$d' file                 # 最終行を削除(繰り返しで複数行)

実例 — 高度な使い方

# キャプチャグループの利用
sed -E 's/^([0-9]+) ([a-z]+)/\2:\1/' file
# 入力: "42 hello" → 出力: "hello:42"

# 行番号を行頭に付ける
sed = file | sed 'N;s/\n/: /'

# 改行を含む置換(GNU sed の z モード - null 区切り処理)
sed -z 's/foo\nbar/baz/g' file

# 複数コマンドを 1 度に
sed -e 's/foo/bar/g' -e '/^$/d' -e '1d' file

# 行末改行で grep -A の代用(パターン以降を出力)
sed -n '/MARKER/,$p' file
sed -i 's/.../.../' で「思ったところ以外まで置換した」事故が頻発します。 まず sed 's/.../.../' (-i なし)で標準出力に流して確認、それから -i を付ける癖を。 Git 管理されているなら直後に git diff で差分確認。

3. awk — フィールド単位の処理・集計

「行を空白で区切ったフィールド」として扱える小さなスクリプト言語。 CSV/TSV/ログの集計、列の入れ替え、合計・平均算出などはこれが最強です。

基本構文

awk 'パターン { アクション }' ファイル$1, $2, ... がフィールド、$0 が行全体、NF がフィールド数、NR が行番号。

変数意味
$0行全体
$1, $2, ...1, 2, ... 番目のフィールド
$NF最後のフィールド
NFその行のフィールド数
NR累積行数(処理中のレコード番号)
FNR現ファイル内の行番号(複数ファイル時)
FS入力フィールド区切り(デフォルト空白)
OFS出力フィールド区切り
RSレコード区切り(デフォルト改行)
FILENAME処理中のファイル名

実例 — 列抽出

# 1 列目だけ表示
awk '{ print $1 }' file

# 複数列を入れ替え
awk '{ print $3, $1 }' file

# CSV を扱う(区切り指定)
awk -F, '{ print $2 }' data.csv
awk -F, 'NR>1 { print $2 }' data.csv     # ヘッダをスキップ

# 区切りを変えて出力
awk 'BEGIN{OFS=","} { print $1,$3 }' data.tsv

# 最後の列を表示
awk '{ print $NF }' file

# 行番号を付けて出力
awk '{ printf "%5d  %s\n", NR, $0 }' file

実例 — 条件抽出

# 特定の値を持つ行だけ
awk '$3 == "ERROR"' app.log
awk '$5 > 1000' file              # 5 列目が 1000 超
awk 'NF > 2' file                 # フィールド数 3 以上の行
awk '/pattern/ && $2 > 100' file  # 正規表現 + 数値条件
awk 'NR % 2 == 0' file            # 偶数行のみ
awk 'NR >= 100 && NR <= 200' file # 行範囲
awk 'length($0) > 80' file        # 80 文字超の行

実例 — 集計

# 合計
awk '{ sum += $1 } END { print sum }' nums.txt

# 平均(小数表示)
awk '{ sum += $1; n++ } END { printf "%.2f\n", sum/n }' nums.txt

# 列ごとの合計
awk '{ a += $1; b += $2 } END { print a, b }' file

# カテゴリ別カウント(連想配列)
awk '{ count[$1]++ } END { for (k in count) print k, count[k] }' file

# 最大値・最小値
awk 'NR==1 || $1 > max { max=$1 } END { print max }' file
awk 'NR==1 { min=$1 } $1 < min { min=$1 } END { print min }' file

# CSV からカテゴリ別合計(ピボット)
awk -F, 'NR>1 { sales[$1] += $3 } END { for (k in sales) print k, sales[k] }' sales.csv

実例 — ログ解析の典型

# nginx access.log から HTTP ステータスコード別カウント
awk '{ print $9 }' access.log | sort | uniq -c | sort -rn

# レスポンスサイズの平均
awk '{ sum += $10; n++ } END { print sum/n }' access.log

# 5xx エラーのリクエストパスをトップ 10
awk '$9 ~ /^5/ { print $7 }' access.log | sort | uniq -c | sort -rn | head -n 10

# 1 時間ごとのリクエスト数(時刻フォーマット依存)
awk '{ split($4, a, ":"); print a[2]":"a[3]":00" }' access.log | sort | uniq -c

BEGIN / END ブロック

awk 'BEGIN { print "header"; sum=0 }
     { sum += $1; print NR, $1 }
     END   { print "total:", sum }' nums.txt

4. 列・行操作 (cut / paste / join / column)

cut — 列抽出(軽量版 awk)

cut -f1,3 data.tsv               # タブ区切り、1, 3 列目
cut -d',' -f2-4 data.csv         # CSV、2〜4 列目
cut -d':' -f1 /etc/passwd        # ユーザ名一覧
cut -c1-10 file.txt              # 各行の先頭 10 文字
cut -c10- file.txt               # 11 文字目以降
cut は単純で速い。複雑な処理は awk、CSV 内のクォート対応 ("a,b",c) が必要なら専用ツール (Python csv モジュール、csvkit, miller) を使う。

paste — 列方向に連結

paste a.txt b.txt                  # 1 列目 a / 2 列目 b(タブ区切り)
paste -d, a.txt b.txt              # カンマ区切り
paste -s -d, file                  # 縦並びを横にカンマ連結 (e.g., 1,2,3,4,5)
paste <(cut -f1 data.tsv) <(cut -f3 data.tsv)   # 列の並び替え + 結合

join — 共通キーで結合(SQL の JOIN 相当)

事前に両ファイルともキー列でソートが必要。

sort -k1,1 users.txt -o users.txt
sort -k1,1 orders.txt -o orders.txt
join users.txt orders.txt           # 1 列目(デフォルト)でマッチ
join -t, -1 1 -2 2 a.csv b.csv      # CSV、a の 1 列目と b の 2 列目で join

column — 整形して見やすく

cat /etc/passwd | column -t -s:    # コロン区切り → スペース揃え表示
ls -l | column -t                  # ls -l を縦揃え
mount | column -t

5. 並べ替え・重複 (sort / uniq / shuf)

sort — 並べ替え

オプション意味
-n数値ソート
-h人間可読サイズ (1K, 2M) ソート
-r降順
-k NN 列目をキーに
-t SEP区切り文字
-u重複除外
-Vバージョン番号順 (1.10 が 1.2 の後)
-Rランダム
--parallel=N並列ソート
sort file                      # 辞書順
sort -n nums.txt               # 数値順
sort -nr nums.txt              # 数値順 降順
sort -k2 -n data.txt           # 2 列目を数値ソート
sort -t, -k3 -n data.csv       # CSV の 3 列目を数値ソート
sort -u file                   # 重複除外(uniq の代用)
sort -V versions.txt           # バージョン順
du -sh */ | sort -h            # サイズ順(人間可読)

# 大ファイル安定ソート(メモリ節約 + 並列)
sort --parallel=4 -S 1G huge.txt -o sorted.txt

uniq — 重複処理

事前に sort しないと「連続して同じ行のみ」が対象なので注意。

sort file | uniq               # 重複除去
sort file | uniq -c            # 出現回数付き
sort file | uniq -c | sort -rn # 多い順ランキング(超頻出パターン)
uniq -d file                   # 重複行のみ表示
uniq -u file                   # ユニーク行(一度しか出ない)のみ
sort file | uniq -i            # 大小無視で重複判定

shuf — ランダムサンプリング

shuf file                       # 全行をランダム順
shuf -n 10 file                 # ランダム 10 行
shuf -e a b c d                 # 列挙からランダム
shuf -i 1-100 -n 5              # 1〜100 から 5 個ランダム

6. 変換・カウント (tr / wc / nl / rev)

tr — 文字単位変換

tr 'a-z' 'A-Z' < file              # 小文字→大文字
tr -d '\r' < win.txt > unix.txt    # CR 削除(CRLF→LF)
tr -d ' '                            # 空白を全削除
tr -s ' '                            # 連続空白を 1 個に圧縮
tr ',' '\n' < csv                  # カンマを改行に
echo "Hello World" | tr -dc 'a-zA-Z' # 英字以外を削除

wc — 数を数える

wc -l file              # 行数
wc -w file              # 単語数
wc -c file              # バイト数
wc -m file              # 文字数(マルチバイト対応)
wc -L file              # 最長行の長さ
ls | wc -l              # ファイル数

nl — 行番号を付ける

nl file                          # 空行は番号付けない(デフォルト)
nl -ba file                      # 全行に番号
nl -nrz -w 4 file                # ゼロ埋め 4 桁

rev — 行内の文字を逆転

echo "abcde" | rev               # → edcba
ls *.tar.gz | rev | cut -d. -f1 | rev   # 拡張子だけ抽出

7. 差分・パッチ (diff / comm / patch)

diff — 行差分

diff a.txt b.txt              # 標準形式
diff -u a.txt b.txt           # unified 形式 (git diff と同じ)
diff -y a.txt b.txt           # 横並び
diff -r dir1/ dir2/           # ディレクトリ再帰
diff -q -r dir1/ dir2/        # 差分の有無だけ
diff -i a.txt b.txt           # 大小無視
diff -w a.txt b.txt           # 空白差分を無視
diff <(sort a.txt) <(sort b.txt)  # ソート済みで比較(順序差を吸収)

comm — ソート済み行集合の比較(共通・差分)

# 両ファイルとも事前にソート必須
sort a.txt -o a.txt
sort b.txt -o b.txt

comm a.txt b.txt              # 3 列: a のみ / b のみ / 共通
comm -12 a.txt b.txt          # 共通のみ (intersection)
comm -23 a.txt b.txt          # a にのみある (a - b)
comm -13 a.txt b.txt          # b にのみある (b - a)

patch — パッチ適用

diff -u original.txt new.txt > my.patch     # パッチ作成
patch original.txt < my.patch                # 適用
patch -R original.txt < my.patch             # 適用を戻す
patch -p1 < project.patch                    # ディレクトリ階層 1 つスキップ(git 形式)

よく使う組み合わせパターン

テキスト処理は単体コマンドより パイプ連結で力を発揮します。 現場で頻出するレシピを集めました。

ログ解析パターン

# nginx の HTTP ステータスコード別 カウント
awk '{ print $9 }' access.log | sort | uniq -c | sort -rn

# IP アドレス別アクセス回数 トップ 20
awk '{ print $1 }' access.log | sort | uniq -c | sort -rn | head -n 20

# 5xx エラーが出た URL トップ
awk '$9 ~ /^5/ { print $7 }' access.log | sort | uniq -c | sort -rn | head

# UA ベースの集計(スペース含む)
awk -F'"' '{ print $6 }' access.log | sort | uniq -c | sort -rn

# CloudWatch Logs から取った JSON ログの ERROR 抽出
aws logs filter-log-events --log-group-name /aws/lambda/myfn \
  | jq -r '.events[] | select(.message | contains("ERROR")) | .message'

ファイル一括処理

# プロジェクト全 .tf から module ブロックの名前一覧
grep -h '^module' *.tf | awk '{ print $2 }' | tr -d '"'

# CSV のヘッダ取り除いて 3 列目の合計
tail -n +2 data.csv | awk -F, '{ sum += $3 } END { print sum }'

# 巨大ログから ERROR 行だけ抜き出して別ファイルへ
grep ERROR huge.log | tee errors.log | wc -l

# Markdown ファイル全部から「TODO:」の行を抽出
find . -name '*.md' -exec grep -Hn 'TODO:' {} +

テキスト整形パターン

# Windows 改行 (CRLF) → Unix (LF)
tr -d '\r' < win.txt > unix.txt

# JSON を 1 行にまとめる
cat data.json | tr -d '\n'

# 空行と コメント行を取り除く
sed -e '/^#/d' -e '/^$/d' config.yaml

# CSV のカラム入れ替え
awk -F, 'BEGIN{OFS=","} { print $3,$1,$2 }' data.csv

# ファイルの末尾改行を確認・追加(POSIX 準拠)
[ -z "$(tail -c1 file)" ] || echo >> file

差分・比較パターン

# 2 ファイルの「行集合」差分(順序関係なく)
comm -3 <(sort a.txt) <(sort b.txt)

# IaC 適用前後の terraform plan 差分
terraform show -no-color before.tfplan > before.txt
terraform show -no-color after.tfplan  > after.txt
diff -u before.txt after.txt

# 1 つのリストから他リストにないもの
comm -23 <(sort all.txt) <(sort exclude.txt)

用語集

正規表現 (regex)
文字列パターンを表す記法。.=任意 1 文字、*=0 回以上、+=1 回以上、^=行頭、$=行末、[abc]=いずれか、(...)=グループ。
BRE / ERE / PCRE
正規表現の方言。BRE = 基本(grep デフォルト・+? はエスケープ必要)、ERE = 拡張(grep -E・直感的)、PCRE = Perl 互換(grep -P・先読みなど高機能)。
ストリームエディタ
ファイルを 1 行ずつ読み込みながら処理する設計。sed がその代表。メモリ効率が良いが、複数行にまたがる処理は苦手。
パイプ (|)
あるコマンドの標準出力を別コマンドの標準入力に渡すシェル機能。テキスト処理ではこれを連結して 1 行のレシピを組むのが基本。
プロセス置換 <(...)
コマンドの出力を「ファイルのように扱える」シェル機能。diff <(sort a) <(sort b) のように一時ファイルなしで比較できる。bash/zsh で使える。
FS / OFS / RS
awk の入力フィールド区切り (FS) / 出力フィールド区切り (OFS) / レコード区切り (RS)。CSV を扱う時 FS="," を指定する。
連想配列 (associative array)
キーで値を引ける配列。awk の arr[key] = value。カテゴリ別カウントなどの集計で頻出。
BEGIN / END ブロック
awk で「処理開始前 (BEGIN) / 終了後 (END)」に 1 回だけ実行されるブロック。初期化や合計の最終出力に使う。
キャプチャグループ
正規表現の (...) で囲った部分。後方参照 \1$1 で再利用できる。sed の置換で頻出。
固定文字列マッチ (-F / fgrep)
正規表現として解釈せず、リテラル文字列として検索するモード。メタ文字を含む文字列を grep する時に使う。
ripgrep (rg)
Rust 製のモダンな grep 代替。デフォルトで .gitignore 尊重・並列実行・色付き。`rg` で起動。
unified diff 形式
diff -ugit diff が出す、コンテキスト 3 行付きで +/- で差分を示す形式。`patch` コマンドの標準入力にもなる。
標準ストリーム (stdin / stdout / stderr)
すべてのコマンドが持つ 3 本の入出力。stdin (0) = キーボード or パイプ、stdout (1) = 通常出力、stderr (2) = エラー出力。2>&1 で結合できる。
argument list too long
シェル glob 展開や引数列挙で、プロセスの引数長制限を超えた時のエラー。find ... | xargsfind -exec ... + で回避。
BSD vs GNU coreutils
macOS 標準は BSD 系、Linux 標準は GNU 系で、sed -i, tar, find -printf, grep -P などの挙動が異なる。クロスプラットフォームスクリプトは特に注意。