手順

grep、sed、awk:Linuxターミナルでのテキスト処理の例

更新日: · Skillaria.Top

grep はパターンに一致する行を探し(grep -rn error /var/log)、sed はストリームやファイルの文字列を置換します(sed -i 's/old/new/g' file)。awk は列を扱って集計します(awk '{print $1}' access.log)。パイプラインでつなぐと、3つを組み合わせて簡単なスクリプトの代わりになります。

grep、sed、awk は Linux でテキストを処理する定番の3つのツールです。grep はパターンに一致する行を探し、sed はエディタを開かずにストリーム中の文字列を置換・削除します。awk は行を列に分けて扱い、集計もできます。以下では、ログや設定ファイル、コマンドの出力を調べるときに実際に役立つ grep sed awk の例を、よくあるミスとあわせて紹介します。

どの場面でどのツールを使うか

3つのツールはいずれもテキストを1行ずつ読み、結果を標準出力に書きます。そのため | でつなぐのが簡単です。違いは、それぞれが得意とする処理にあります。

目的ツール例
文字列を含む行を探すgrepgrep ERROR app.log
文字列を置換するsedsed 's/http:/https:/g' file
行番号で行を絞り込む、削除するsedsed -n '10,20p' file
列を取り出すawkawk '{print $1}' access.log
合計、平均、グループ別の集計awkawk '{s+=$10} END {print s}'

簡単な目安は次のとおりです。行を絞り込むだけなら grep、文字列を書き換えるなら sed、列や計算が必要なら awk を使います。

grep:パターンで行を探す

grep の基本的な例

# ファイル内に error という語を含む行
grep error /var/log/syslog

# 大文字小文字を区別しない:Error, ERROR, error
grep -i error app.log

# 行番号を付けて表示
grep -n "Connection refused" app.log

# ディレクトリを再帰的に検索し、ファイル名も表示
grep -rn "DB_HOST" /etc/myapp/

# 一致したファイル名だけを表示
grep -rl "TODO" src/

# 反転:コメントと空行「以外」をすべて表示
grep -v -e '^#' -e '^$' /etc/ssh/sshd_config

# 一致した行数を数える
grep -c " 500 " access.log

-w オプションは単語単位で検索します。grep -w log は login には一致しません。-F オプションは正規表現を無効にして文字列をそのまま検索します。パターンにドット、括弧、アスタリスクが含まれる場合に便利です。

一致した行の前後の文脈を表示する

ログのエラーは1行だけ見ても原因が分からないことが多く、それを引き起こしたスタックトレースやリクエストが必要になります。そのためのオプションが文脈表示です。

# 一致の後ろ3行(After)
grep -A 3 "Traceback" app.log

# 一致の前2行(Before)
grep -B 2 "OOM" /var/log/kern.log

# 前後それぞれ5行(Context)
grep -C 5 "segfault" /var/log/syslog

grep は一致したまとまりの間に -- の行を入れるので、出力を目で追いやすくなります。

grep と正規表現

grep は既定では基本正規表現(BRE)を扱います。-E オプションを付けると拡張正規表現(ERE)になり、+、?、|、括弧をバックスラッシュなしで使えます。

記法意味例
^行頭grep '^root' /etc/passwd
$行末grep 'bash$' /etc/passwd
.任意の1文字grep 'h.t'
*0回以上の繰り返しgrep 'ab*c'
+(ERE)1回以上の繰り返しgrep -E '[0-9]+'
|(ERE)またはgrep -E 'error|warn'
[...]指定した集合の中の1文字grep '[Ee]rror'
# エラーまたは警告を含む行
grep -E "ERROR|WARN" app.log

# IP アドレスだけを1行ずつ表示(-o は一致部分だけを出力)
grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' access.log

grep -P:既知の文字列に挟まれた値を取り出す

GNU grep には Perl 風の正規表現を使う -P オプションがあります。\K(ここまでの一致を捨てる)と先読みを使うと、値だけを取り出せます。

$ echo 'user_id=4821 status=ok' | grep -oP 'user_id=\K[0-9]+'
4821

$ grep -oP '"version":\s*"\K[^"]+' package.json
2.4.1

-P はすべての grep で使えるわけではありません(例えば macOS の BSD grep にはありません)。移植性が必要なスクリプトでは sed や awk を使ってください。

スクリプトの条件での grep -q

grep -q は何も表示せず、終了コードだけを返します。見つかれば 0、見つからなければ 1 です。そのまま if の条件に書けます。

if grep -q "^PermitRootLogin no" /etc/ssh/sshd_config; then
  echo "root でのログインは無効です"
fi

結果を変数に保存して空かどうか調べるより短く書けます。また、grep は最初に一致した時点で終了します。

sed:文字列の置換と削除

置換:s コマンド

sed の基本は s/パターン/置換後/フラグ です。フラグ g を付けないと、各行で最初の一致しか置換されません。

# 置換後の結果を表示(ファイルは変更しない)
sed 's/foo/bar/' file.txt

# 行内のすべての一致を置換
sed 's/foo/bar/g' file.txt

# 大文字小文字を区別しない(GNU sed)
sed 's/error/ERROR/gI' app.log

# スラッシュを含む場合は区切り文字を変える
sed 's|/var/www|/srv/www|g' nginx.conf

sed -i:ファイルを直接書き換える

-i オプションは結果をファイルに書き戻します。元のファイルを失わないように、バックアップの接尾辞を指定しておくと安心です。

# config.ini.bak を作成してから config.ini を変更
sed -i.bak 's/^debug=true/debug=false/' config.ini

# ディレクトリ内のすべての .conf を置換
find /etc/myapp -name "*.conf" -exec sed -i 's/old-host/new-host/g' {} +

大量に書き換える前には、必ず -i を付けずに実行して出力を確認してください。macOS(BSD sed)では -i に引数が必須です。例:sed -i '' 's/a/b/' file。

行番号やパターンで行を指定する

# 10〜20行目を表示(-n は自動出力を無効にし、p は出力)
sed -n '10,20p' file.txt

# 5行目を削除
sed '5d' file.txt

# 最後の行を削除
sed '$d' file.txt

# 空行とコメント行を削除
sed -e '/^$/d' -e '/^#/d' nginx.conf

# 「listen」を含む行だけで 80 を 8080 に置換
sed '/listen/s/80/8080/' nginx.conf

# パターンに一致した行の後ろに追加(GNU sed)
sed '/^\[mysqld\]/a max_connections = 500' my.cnf

後方参照と &

パターン中の括弧で囲んだ部分は、置換側で \1、\2 として参照できます。記号 & は一致した文字列全体を表します。

# 姓と名を入れ替える(Yamada Taro → Taro Yamada)(-E で拡張正規表現)
$ echo "Yamada Taro" | sed -E 's/(\S+) (\S+)/\2 \1/'
Taro Yamada

# すべての数字を角括弧で囲む
$ echo "port 8080, timeout 30" | sed -E 's/[0-9]+/[&]/g'
port [8080], timeout [30]

# 日付 2026-10-14 を 14.10.2026 に変換
$ echo 2026-10-14 | sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3.\2.\1/'
14.10.2026

awk:列、条件、集計

必要な列を出力する

awk は各行をスペースとタブで区切って欄に分けます。$1 が1番目の欄、$2 が2番目、$NF が最後の欄、$0 が行全体です。

# nginx のログから IP アドレス(1列目)を取り出す
awk '{print $1}' /var/log/nginx/access.log

# /etc/passwd からユーザー名とシェルを取り出す(区切りはコロン)
awk -F: '{print $1, $7}' /etc/passwd

# 最後の列
df -h | awk '{print $NF}'

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

条件:必要な行だけを処理する

波括弧の前に条件を書くと、その条件に合う行だけに処理が適用されます。

# UID が 1000 以上のユーザー(システムユーザーを除く)
awk -F: '$3 >= 1000 {print $1}' /etc/passwd

# ステータスコードが 500 以上のリクエスト(combined 形式の9番目の欄)
awk '$9 >= 500' access.log

# 6番目の列が正規表現に一致する行
awk '$6 ~ /POST/' access.log

# ls -l の結果から 100 MB を超えるファイルを表示
ls -l | awk '$5 > 100*1024*1024 {print $9}'

BEGIN、END、NR:小さなプログラムとしての awk

BEGIN ブロックは最初の行を読む前に、END ブロックは最後の行を読んだ後に実行されます。NR は現在の行番号を保持し、END ブロックでは行の総数になります。

# 応答サイズ(10番目の欄)の合計をメガバイトで表示
awk '{sum += $10} END {printf "%.1f MB\n", sum/1024/1024}' access.log

# 最後の列(応答時間)の平均
awk '{s += $NF} END {if (NR) print s/NR}' timings.log

# CSV のヘッダー行を飛ばす
awk -F, 'NR > 1 {print $2}' users.csv

# IP ごとのリクエスト数:連想配列を使う
awk '{count[$1]++} END {for (ip in count) print count[ip], ip}' access.log | sort -rn | head

最後の例はログ解析の定番です。キーを IP とする配列 count が sort | uniq -c の組み合わせを置き換え、IP ごとの転送量の合計などにも簡単に拡張できます。

パイプライン:grep、sed、awk の組み合わせ

3つを組み合わせると真価を発揮します。各ツールが役割を分担し、| が結果を次に渡します。

5xx エラーを多く出している IP のトップ10

grep ' 50[0-9] ' /var/log/nginx/access.log \
  | awk '{print $1}' \
  | sort | uniq -c | sort -rn | head -10
    412 203.0.113.45
    118 198.51.100.7
     36 192.0.2.19

ここでは grep が大まかに行を絞り込み、awk が欄を取り出し、sort | uniq -c が重複を数えます。同じ処理を awk 1つで書くこともできます:awk '$9 ~ /^5/ {c[$1]++} END {for (i in c) print c[i], i}' access.log | sort -rn | head。こちらは応答コードの欄を直接調べるので、より正確です。

よく開かれている URL

awk '{print $7}' access.log | sed 's/?.*//' | sort | uniq -c | sort -rn | head

sed はクエリ文字列を取り除きます。これにより /search?q=a と /search?q=b が同じページとして数えられます。

コメントを除いた設定値

grep -v '^\s*#' /etc/myapp/app.env | grep '=' | awk -F= '{print $1}'

特定の1時間のエラー

grep '14/Oct/2026:03:' access.log | awk '$9 >= 500 {print $4, $7, $9}'

よくあるミス

  • awk のプログラムを二重引用符で囲む。awk "{print $1}" では、awk が起動する前にシェルが自分の変数 $1 を展開してしまいます。awk のプログラムや sed の式は一重引用符で囲んでください。
  • sed で g を忘れる。これを付けないと、各行の最初の一致しか置換されません。
  • 確認せずに sed -i を使う。不適切な正規表現はファイルを静かに壊します。まず -i なしで実行するか、-i.bak を使ってください。
  • grep で + や | が効かない。これは拡張構文なので grep -E(または sed -E)が必要です。
  • パターン中のドット。grep 192.168.1.1 は 192x168y1z1 にも一致します。ドットは「任意の1文字」だからです。grep -F を使うか、\. でエスケープしてください。
  • 列にスペースが含まれる。欄にスペースが入りうる場合(CSV やパス)は、区切り文字を明示してください。例:awk -F, や awk -F'\t'。
  • 無駄な cat。cat file | grep x も動きますが、grep x file の方が短く、余分なプロセスも生成しません。

grep、sed、awk の代わりになるツール

JSON をこれらのツールで扱うのは大変です。構造は jq(例:jq '.items[] | select(.status=="active") | .name')で、YAML は yq で扱うほうが確実です。大規模なプロジェクトを素早く再帰検索するなら、既定で .gitignore を尊重する ripgrep(コマンド rg)がよく使われます。Perl の構文に慣れているなら、perl -pe 's/a/b/g' と perl -lane 'print $F[0]' が1行で sed と awk の代わりになります。ただし grep、sed、awk はほぼすべてのサーバーに最初から入っており、インストールも不要です。だからこそ最初に覚えておく価値があります。

まとめ

grep は行を探し、sed はそれを書き換え、awk は列を扱って集計します。基本セットとして覚えておきたいのは、grep -rniE と -A/-B/-C、sed 's/.../.../g' と -i.bak、-n 'Np'、awk -F と $1、$NF、条件、END ブロックです。このような部品を組み合わせれば、ほとんどのログ解析が書けます。ここで紹介した grep sed awk の例は、find、jq、curl とあわせて、Linux コースのテキスト処理の講義で実際に試せます。

よくある質問

grep と egrep の違いは何ですか?

egrep は grep -E(拡張正規表現)の古い別名です。現在の GNU grep では egrep を呼ぶと警告が出るので、grep -E と書いてください。

ディレクトリ内のすべてのファイルから文字列を探すには?

grep -rn "文字列" /パス を使います。-r は再帰的に検索し、-n は行番号を表示します。ファイルの種類を絞るには --include="*.php" を追加します。

多数のファイルの文字列をまとめて置換するには?

検索と sed を組み合わせます:grep -rl "old" . | xargs sed -i 's/old/new/g'。実行前にコミットやバックアップを取っておくと安心です。

awk で複数の列をタブ区切りで出力するには?

出力区切り文字を指定します:awk -v OFS='\t' '{print $1, $3}' file。OFS を指定しないと、カンマで区切った欄はスペースで区切られます。

sed で行末の空白を削除するには?

sed -i 's/[[:space:]]*$//' file を実行すると、ファイル内のすべての行の末尾にあるスペースとタブが削除されます。

動画でこのテーマを学ぶ

このテーマはコース「Linux: с нуля до уверенного администрирования」で実践とともに詳しく解説しています。

コースへ