sed 入門 - ストリームエディタでテキスト置換

sed 入門 - ストリームエディタでテキスト置換

この記事で解決できること

  • sed代表 5 パターン(置換 / 削除 / 印字 / 範囲 / 複数式)を最短ルートで習得できる
  • -i インプレース編集の 事故を避ける型 が身につく
  • BRE と ERE(-E)の違いを把握し、grep -E / awk と一貫した正規表現が書けるようになる

結論(実務で使う型)

  • まず 標準出力で確認 → 問題なければ -i.bak で上書き
  • 区切り文字は / 以外も使える(パスを置換するときは |#
  • 「全置換したい」なら必ず末尾 gg を忘れると 行内 1 件のみ 置換

前提(対象環境)

  • OS: Ubuntu / Debian / RHEL 系 Linux
  • 想定 sed: GNU sed(macOS の BSD sed は -i の書式が異なるため別途注意)

sed とは?

sed(stream editor)は 入力ストリームを 1 行ずつ読み、編集スクリプトを適用して出力する コマンド。対話編集の Vim と違い、シェルパイプライン上で非対話に動く のが本質。ログ整形・設定ファイル一括書き換え・テキスト前処理で使う。

$ echo "hello world" | sed 's/world/Linux/'
hello Linux

s/old/new/substitute(置換) コマンドで、sed の使用頻度の 8 割を占める。

なぜ最初に「標準出力で確認」が鉄則なのか?

sed -iファイルを直接書き換える。書き換えた後で「置換パターンを間違えた」「区切り文字に含まれる / をエスケープし忘れた」と気づくと復旧できない。標準出力に出して 目で確認してから -i を付けるのが事故防止の唯一の型。

# 1. まず標準出力で確認
$ sed 's/foo/bar/g' config.txt

# 2. 問題なければバックアップ付きで上書き
$ sed -i.bak 's/foo/bar/g' config.txt

# 3. config.txt.bak が自動生成されている
$ ls config.txt*
config.txt  config.txt.bak

-i 単独(バックアップなし)は本番運用では非推奨。-i.bak を癖にすると、想定外置換からのロールバックが mv config.txt.bak config.txt で済む。

1. 置換: s/old/new/ の基本形

1-1. 行内最初の 1 件だけ置換(デフォルト)

$ echo "apple apple apple" | sed 's/apple/orange/'
orange apple apple

1-2. 行内すべて置換(g フラグ)

$ echo "apple apple apple" | sed 's/apple/orange/g'
orange orange orange

g の付け忘れは sed 初心者の最頻出ミス。 「1 行に複数ある置換対象が一部しか変わらない」と感じたらまず g を確認する。

1-3. 大文字小文字を無視(I フラグ)

$ echo "Apple APPLE apple" | sed 's/apple/orange/gI'
orange orange orange

I フラグは GNU sed 拡張。POSIX sed には存在しないため、移植性が必要な場面では使わない。

2. 区切り文字を / 以外に変える

置換対象にパス(/usr/local)が含まれると / のエスケープ地獄になる。s 直後の文字が区切り文字になる ため、| # , などを選べる。

# NG: エスケープが必要で読みにくい
$ sed 's/\/usr\/local\/bin/\/opt\/bin/' file

# OK: パイプを区切り文字に
$ sed 's|/usr/local/bin|/opt/bin|' file

# OK: # を区切り文字に
$ sed 's#/usr/local/bin#/opt/bin#' file

区切り文字を変えるだけでバグの 8 割は消える。パス置換は |# がデファクト。

3. アドレス指定(範囲を絞る)

sed の編集コマンドは アドレス(範囲)を前置 することで対象行を絞れる。

3-1. 行番号で指定

$ sed '3s/foo/bar/' file       # 3 行目のみ
$ sed '1,5s/foo/bar/g' file    # 1〜5 行目
$ sed '10,$s/foo/bar/g' file   # 10 行目〜最終行($ = EOF)

3-2. パターンで指定

# "ERROR" を含む行だけ置換
$ sed '/ERROR/s/foo/bar/g' file

# /START/ から /END/ までの範囲で置換
$ sed '/START/,/END/s/foo/bar/g' file

3-3. 否定(!

# 1 行目「以外」を置換
$ sed '1!s/foo/bar/g' file

# コメント行(#始まり)以外で置換
$ sed '/^#/!s/foo/bar/g' file

4. 削除(d)と印字(p)

4-1. 行削除

$ sed '/^$/d' file              # 空行を削除
$ sed '/^#/d' file              # コメント行(# 始まり)を削除
$ sed '1,5d' file               # 1〜5 行目を削除
$ sed '$d' file                 # 最終行を削除

4-2. 特定行のみ印字(-n + p)

sed のデフォルトは「すべての行を出力 + 編集適用」。-nデフォルト出力を抑止 し、p コマンドで明示的に出力する。

# "ERROR" を含む行だけ出力(grep "ERROR" と同等)
$ sed -n '/ERROR/p' file

# 10〜20 行目だけ出力
$ sed -n '10,20p' file

sed -n 'Np'head -n N | tail -1 より高速で読みやすい。

5. 複数の編集を一度に適用

5-1. -e で複数式

$ sed -e 's/foo/bar/g' -e 's/hoge/fuga/g' file

5-2. セミコロンで連結(GNU sed)

$ sed 's/foo/bar/g; s/hoge/fuga/g' file

5-3. スクリプトファイルから読み込み

繰り返し使う変換セットはファイルに保存できる。

$ cat replace.sed
s/foo/bar/g
s/hoge/fuga/g
/^#/d

$ sed -f replace.sed file

6. BRE と ERE:-E を使うべきか?

sed はデフォルトで BRE(基本正規表現) を使う。+ ? | (...) を使うには バックスラッシュでエスケープ が必要で読みにくい。-E を付けると ERE(拡張正規表現) に切り替わり、grep -E / awk と同じ記法になる。

6-1. BRE(デフォルト)

# グループとキャプチャに \( \) が必要
$ echo "2026-05-20" | sed 's/\([0-9]\{4\}\)-\([0-9]\{2\}\)-\([0-9]\{2\}\)/\3\/\2\/\1/'
20/05/2026

6-2. ERE(-E

# エスケープ不要で読みやすい
$ echo "2026-05-20" | sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3\/\2\/\1/'
20/05/2026

実務では -E を癖にする。 BRE のエスケープは読み手の負担が大きく、レビューでの取りこぼし原因になる。

6-3. 後方参照(\1 \2 ...)

キャプチャした部分は置換側で \1 \2 ... で参照できる。BRE / ERE どちらでも記法は同じ。

# Last, First → First Last に並び替え
$ echo "Doe, John" | sed -E 's/(.+), (.+)/\2 \1/'
John Doe

7. やってはいけない・事故パターン

事故パターン Top 5

  1. -i を確認なしで実行 → 復旧不能。必ず -i.bak か事前 dry-run
  2. g 忘れで全置換できない → 行内 1 件しか変わらない
  3. 区切り文字エスケープ漏れ|# に切り替えれば回避
  4. BRE/ERE 混乱 → 統一して -E を使う
  5. * . をリテラルとして扱おうとする → 正規表現のメタ文字なのでエスケープ必要

7-1. リテラル文字のエスケープ

# NG: . が「任意の 1 文字」として解釈される
$ echo "192x168x0x1" | sed 's/192.168.0.1/X/'
X

# OK: . をエスケープ
$ echo "192x168x0x1" | sed 's/192\.168\.0\.1/X/'
192x168x0x1

7-2. シェル変数を埋め込むとき

シングルクォート内ではシェル変数は展開されない。ダブルクォートに切り替えるが、$\ の扱いに注意する。

NEW="bar"

# NG: $NEW がそのまま文字列扱い
$ sed 's/foo/$NEW/g' file

# OK: ダブルクォートで展開
$ sed "s/foo/$NEW/g" file

$NEW の中身に / & \ が含まれると壊れる。安全に変数展開したいなら区切り文字を別の文字に変えるか、printf '%s' "$NEW" でサニタイズする。

8. 実務で使えるレシピ集

コピペ用テンプレート

# CRLF を LF に変換
sed -i.bak 's/\r$//' file

# 空行と # で始まるコメント行を除去
sed -e '/^$/d' -e '/^#/d' /etc/nginx/nginx.conf

# IP アドレスをマスク
sed -E 's/\b([0-9]{1,3}\.){3}[0-9]{1,3}\b/x.x.x.x/g' access.log

# 末尾の空白を削除
sed -i.bak 's/[[:space:]]*$//' file

# 指定行の前後に行を挿入
sed -i.bak '/^pattern/i\
inserted line before' file

# YAML の値だけ書き換え(key: の後ろ)
sed -i.bak -E 's/^(version:\s*).*/\1"1.2.3"/' config.yaml

比較: sed と awk / grep の使い分け

用途 推奨
単純な文字列置換 sed
行抽出(grep 互換) grep / sed -n
列単位の処理(カラム抽出・計算) awk
複数行にまたがる変換 sed の N コマンド or awk
構造化データ(JSON/YAML)の編集 jq / yq

sed の強み: ストリーミング 1 行処理、軽量、ほぼすべての Unix で利用可能 sed の限界: 列処理・構造化データ・多行処理が苦手。複雑になったら awk / jq に切り替える

次に読む