【Linux】sedコマンドの使い方|文字列置換・行抽出・-iで直接編集

【Linux】sedコマンドの使い方|文字列置換・行抽出・-iで直接編集 Linux

sed(Stream EDitor)は、テキストを1行ずつ処理して、文字列の置換や行の抽出・削除を行うLinuxの定番コマンドです。もっともよく使うのが文字列の置換で、ファイルの中の特定の文字を一括で書き換えたり、設定ファイルの値を変更したりできます。grepが「検索」、sedは「置換・編集」と覚えると分かりやすいです。

つまずきやすいのは、置換が「各行で最初に見つかった1つ」だけになること(すべて置換するにはgが必要)と、-iを付けるとファイルが直接書き換わる(取り消せない)ことです。この記事では、実機のLinux(WSLのDebian)で実際にコマンドを動かしながら、sedの使い方を整理します。

先に結論

  • 置換はsed "s/古い/新しい/" ファイル。ただし各行で最初の1つだけ置換されます。
  • 行内のすべてを置換するには末尾にgs/古い/新しい/g)を付けます。
  • 大文字小文字を無視するにはis/../../gi)を付けます。
  • パスなど/を含む置換は、区切り文字を|などに変えると書きやすいです。
  • -iを付けるとファイルを直接書き換えます(取り消し不可・要注意)。
  • 行の抽出は-n "2,3p"、行の削除は"2d"です。

検索のgrepコマンド、ファイル検索のfindコマンド、ほかの基本はよく使うLinuxコマンドまとめもあわせて参考になります。

スポンサーリンク

sedの基本:置換 s///

もっとも基本的な使い方はsed "s/古い文字列/新しい文字列/"です。sは置換(substitute)を表し、スラッシュで区切って「何を」「何に」置き換えるかを指定します。

基本の置換
# ファイルの中の apple を orange に置換して表示
sed "s/apple/orange/" fruits.txt

# パイプで受け取った文字列を置換
echo "hello world" | sed "s/world/Linux/"     # hello Linux

# 元のファイルは変更されず、結果が画面に表示されるだけ
# (ファイルを書き換えるには -i が必要・後述)

実機でも、echo "hello world" | sed "s/world/Linux/"hello Linuxを返しました。sed "s/古い/新しい/"で置換ができます。重要なのは、この時点では元のファイルは変更されず、置換した結果が画面に表示されるだけという点です。まずはこの形で結果を確認し、問題なければ後述の-iでファイルに反映する、という流れが安全です。

行内で全部置換するには /g

ここが最初の注意点です。sed "s/../../"は、各行で最初に見つかった1つだけを置換します。行内のすべての一致を置換するには、末尾にg(global)を付けます。

g で全置換
# g なし: 各行で最初の1つだけ置換
echo "apple apple apple" | sed "s/apple/X/"
#  → X apple apple(最初だけ)

# g あり: 行内すべてを置換
echo "apple apple apple" | sed "s/apple/X/g"
#  → X X X(全部)
gを付けないと最初の1つしか置換されない

実機で確認したところ、echo "apple apple" | sed "s/apple/X/"X apple(最初の1つだけ置換)になり、gを付けたs/apple/X/gX X(すべて置換)になりました。sedの置換は、デフォルトでは各行の最初の一致だけを対象にします。「置換したのに一部しか変わっていない」というときは、gの付け忘れが原因のことがほとんどです。行内のすべてを置き換えたいなら、末尾にgを必ず付けてください。逆に「各行で最初の1つだけ」を狙って置換したいときは、あえてgを付けません。1行に1回しか出てこない文字列なら、どちらでも結果は同じです。

大文字小文字を無視・区切り文字の変更

大文字小文字を区別せずに置換するにはiを付けます(gと併せてgi)。また、パスのように/を含む文字列を置換するときは、区切り文字を/以外(|など)に変えると、エスケープが不要で読みやすくなります。

大小無視と区切り文字
# i: 大文字小文字を無視(apple も APPLE も置換)
echo "apple APPLE" | sed "s/apple/X/gi"     # X X

# 区切り文字は / 以外でもよい(パスの置換に便利)
echo "/usr/local/bin" | sed "s|/usr|/opt|"  # /opt/local/bin

# / を区切りに使うと \/ とエスケープが必要でわずらわしい
# echo "/usr/local" | sed "s/\/usr/\/opt/" 

実機でも、s/apple/X/giappleAPPLEの両方をXに置換し、s|/usr|/opt|/usr/local/bin/opt/local/binに置換しました。区切り文字は最初のsの直後の文字で決まるため、s|...|...|のように|を使えば、パスの中の/をエスケープせずに書けます。ファイルパスやURLを置換するときは、この区切り文字の変更が非常に便利です。

-iで直接編集(注意)

ここまでは結果を画面に表示するだけでしたが、-iを付けると、ファイルそのものを直接書き換えます。便利ですが、取り消せないため注意が必要です。

-i でファイルを直接編集
# -i: ファイルを直接書き換える(表示ではなく保存される)
sed -i "s/古い/新しい/g" config.txt

# 安全な手順:
# STEP1 まず -i なしで結果を確認
sed "s/古い/新しい/g" config.txt
# STEP2 問題なければ -i を付けて本実行
sed -i "s/古い/新しい/g" config.txt

# バックアップを残す(-i.bak で config.txt.bak が作られる)
sed -i.bak "s/古い/新しい/g" config.txt
-iは元に戻せない・先に-iなしで確認

実機で確認したところ、-iを付けずに実行したときは元のファイルは変更されず-iを付けたときだけファイルの中身が実際に書き換わりました(lineLINEに置換され、保存されました)。-i(in-place)は直接ファイルを上書きするため、取り消せません。置換条件を間違えると、ファイルが壊れてしまう恐れがあります。必ず先に-iを付けずに実行して、置換結果を画面で確認してから、-iを付けて本実行してください。さらに安全にするには、sed -i.bak "..."のように-iのあとに拡張子を付けると、元のファイルがファイル名.bakとしてバックアップされます。大切なファイルを編集するときは、この方法でバックアップを残すのがおすすめです。

行の抽出・削除(p / d)

sedは置換だけでなく、特定の行を抽出したり削除したりもできます。-n "2,3p"で2〜3行目を表示、"2d"で2行目を削除します。

行の抽出と削除
# -n と p: 2〜3行目だけを表示(-n は自動表示を止める)
sed -n "2,3p" file.txt

# d: 2行目を削除して表示
sed "2d" file.txt

# パターンに一致する行だけ表示(grep のような使い方)
sed -n "/ERROR/p" app.log

# パターンに一致する行を削除
sed "/DEBUG/d" app.log

実機でも、sed -n "2,3p" file.txtは2〜3行目(line2line3)だけを表示し、sed "2d"は2行目を除いた行を表示しました。-nは「自動で全行を表示する」動作を止めるオプションで、p(print)と組み合わせて「指定した行だけ表示」します。sed -n "/ERROR/p"のようにパターンを指定すると、grepのように一致行だけを表示でき、"/DEBUG/d"で特定パターンの行を削除できます。行番号でも正規表現でも指定できるのがsedの柔軟なところです。

拡張正規表現 -E

sedのパターンは標準では基本正規表現(BRE)ですが、-Eを付けると拡張正規表現(ERE)になり、+?|などをそのまま使えます。

拡張正規表現で置換
# -E: 拡張正規表現(+ ? | () をそのまま使える)
echo "foo123bar" | sed -E "s/[0-9]+/#/"     # foo#bar

# 連続する数字をまとめて置換
echo "a1b22c333" | sed -E "s/[0-9]+/N/g"    # aNbNcN

# 複数のパターンを | で(error または warning を大文字に)
sed -E "s/error|warning/[!]/g" app.log

実機でも、sed -E "s/[0-9]+/#/"foo123barの連続する数字123をまとめて#に置換し、foo#barになりました。-Eを付けないと+はそのままの文字として扱われるため、[0-9]+のような「1個以上」を表すパターンでは-Eが必要です。複雑な置換をするときは、-Eを付けて拡張正規表現を使うと書きやすくなります。

主なオプション・書き方一覧

sedでよく使うものをまとめます。

書き方 働き
s/古い/新しい/ 各行で最初の1つを置換
s/古い/新しい/g 行内のすべてを置換
s/../../gi 大文字小文字を無視して置換
s|古い|新しい| 区切り文字の変更(パス向き)
-i ファイルを直接編集(要注意)
-n "2,3p" 指定した行を抽出
"2d" / "/語/d" 行番号 / パターンで行を削除
-E 拡張正規表現を有効にする

よくある失敗

gを付けず一部しか置換されない

各行で最初の1つだけ置換されます。すべて置換するには末尾にgを付けます。

-iで確認せずファイルを壊す

取り消せません。先に-iなしで結果を確認し、-i.bakでバックアップを残します。

パスの / をエスケープしてわかりにくくなる

区切り文字を|などに変えると、エスケープ不要で読みやすくなります。

+ や ? がそのまま扱われる

拡張正規表現には-Eが必要です。付けないと記号がそのままの意味になります。

置換文字列にスラッシュを含めて混乱する

置換内容に/が入るときも、区切り文字を変えると簡単です。

よくある質問

Qsedで文字列を置換するには?
Ased "s/古い文字列/新しい文字列/" ファイルを使います。ただし各行で最初に見つかった1つだけが置換されます。行内のすべてを置換したいときは、末尾にgを付けてs/古い/新しい/gとします。この時点では元のファイルは変更されず、結果が画面に表示されるだけです。
Q置換したのに一部しか変わりません。
Asedの置換は、デフォルトでは各行で最初の1つだけを対象にします。行内のすべてを置換するには、末尾にg(global)を付けてください。s/apple/X/gのようにすると、その行のappleがすべてXに置き換わります。
Qsedでファイルを直接書き換えるには?
A-iオプションを付けます。sed -i "s/古い/新しい/g" ファイルで、ファイルの中身が直接書き換わります。ただし取り消せないため、先に-iなしで結果を確認してください。-i.bakとすると、元のファイルがファイル名.bakとしてバックアップされます。
Qパスなどスラッシュを含む文字列を置換するには?
A区切り文字を/以外に変えると簡単です。sed "s|/usr|/opt|"のように|を使えば、パスの中の/をエスケープせずに書けます。区切り文字はsの直後の文字で決まるため、内容に応じて|#などを使い分けられます。
Q特定の行だけ表示・削除するには?
A抽出はsed -n "2,3p"(2〜3行目を表示)、削除はsed "2d"(2行目を削除)です。パターンでも指定でき、sed -n "/ERROR/p"で一致行を表示、sed "/DEBUG/d"で一致行を削除できます。

まとめ

  • 置換はsed "s/古い/新しい/"各行で最初の1つだけ置換されます。
  • 行内すべてを置換するには末尾にgを付けます。
  • 大小無視はi、パスの置換は区切り文字を|などに変えると便利です。
  • -iはファイルを直接編集(取り消し不可)。先に-iなしで確認します。
  • 行の抽出は-n "2,3p"、削除は"2d""/語/d"です。

sedは、テキストの置換・編集をコマンド1つで行える強力なツールです。「全置換にはg」「-iは確認してから」の2点さえ押さえれば、設定ファイルの一括変更やログの整形を効率よくこなせます。まずは-iなしの置換で結果を確かめながら、少しずつ使いこなしていきましょう。