現場で使えるシェルスクリプト入門14:sedコマンドによる文字列置換と行削除の基本

シェルスクリプト IT

前回の記事(入門13)では、自作のスクリプトで一般的なコマンドのように引数を受け取れるようにする「getopts」でのオプション解析について解説しました。

今回は、プログラムの中でファイルを直接開くことなく、テキストの文字を自動で書き換えたり、不要な行を一括で消し去ったりできる便利なコマンド「sed」の基本的な使い方を学びましょう。

サーバー構築時の設定ファイルの自動書き換えや、バッチ処理でのデータ加工など、現場での自動化において外すことのできない必須コマンドの基本を整理して解説します。

sed コマンドとは?

sed(セド・セッド)は「Stream Editor(ストリームエディタ)」の略で、テキストデータを1行ずつ読み込みながら、指定したルールに従って編集や抽出を行って出力するコマンドです。

画面でファイルを開いて手動で書き換える必要がないため、シェルスクリプトの中に記述して「設定ファイルの値を一括で書き換える」といった自動処理を行うのに向いています。

文字列を「置換(s)」する基本パターン

sed の中で最も頻繁に使われるのが、特定の文字列を別の文字列に変える「置換」の機能です。

sed 's/置換前/置換後/g' file.txt

この基本構文に含まれる各要素には、それぞれ以下のような役割があります。

  • s : 「Substitute(置換)」を意味する命令です。これによって置換モードになります。
  • /(スラッシュ) : 文字列の区切りを示す記号です。「s/置換前/置換後/」のように、置換前と置換後の文字列をスラッシュで挟んで指定します。
  • g : 「Global(全体)」を意味するフラグです。これを付けると、1行の中に同じ「置換前」の文字が複数あった場合、そのすべてを置換します。(付けない場合は、各行の最初に見つかった文字だけが置換されます)

💡 応用:スラッシュ(/)以外の区切り文字も使える

通常、置換コマンドでは区切り文字として /(スラッシュ)を使用しますが、実はスラッシュ以外の任意の1文字(例:|_@ など)も区切り文字として使用できます。

例えば、ファイルパス(例: /usr/local/bin)などのスラッシュを多く含む文字列を置換したい場合、通常の / を使うとすべてのスラッシュの前にエスケープ記号(\)を付けなければならず、コードが非常に読みづらくなってしまいます。

# 1. 通常のスラッシュ区切り(バックスラッシュでのエスケープが必要で見づらい)
sed 's/\/usr\/local\/bin/\/opt\/bin/g' file.txt

# 2. パイプ(|)を区切り文字にした場合(エスケープ不要でスッキリ)
sed 's|/usr/local/bin|/opt/bin|g' file.txt

# 3. アットマーク(@)を区切り文字にした場合
sed 's@/usr/local/bin@/opt/bin@g' file.txt

このように、置換命令 s の直後に指定した文字が自動的に区切り文字として扱われます。特にファイルパスやURLを扱うスクリプトを書く際は、|@ を使って可読性を高めるのが実務の定番テクニックです。

💡 ファイルの中身を直接上書きする「-i」オプション

通常、sed を実行すると結果が画面に出力されるだけで、元のファイル自体は書き換わりません。元のファイルを直接書き換えて保存したい場合は、-i(in-place)オプションを使用します。

# 元のファイル file.txt を直接書き換える
sed -i 's/apple/orange/g' file.txt

設定ファイルなどを自動で書き換える際は、この -i オプションを指定するのが一般的です。もし念のために元のファイルのバックアップを残しておきたい場合は、-i.bak のように記述することで、file.txt.bak という複製を自動で作ることもできます。

💡 特定の「行」や「範囲」を指定して置換する

sed コマンドでは、ファイル全体ではなく、特定の「行番号」や「行の範囲」を指定して文字列を置換することができます。

置換命令である s の直前に、行番号や範囲を指定します。

# 1. 特定の行(例: 3行目)だけを置換する
sed '3s/apple/orange/g' file.txt

# 2. 特定の範囲(例: 2行目から5行目)だけを置換する
sed '2,5s/apple/orange/g' file.txt

# 3. 特定の文字(例: TEST)が含まれる行だけを置換する
sed '/TEST/s/apple/orange/g' file.txt

行番号の代わりに /検索条件/ を指定すれば、特定のキーワードが含まれる行だけに限定して置換を行うことも可能です。これにより、複雑なファイルの書き換えも狙った場所だけをピンポイントで安全に変更できるようになります。

特定の「行」を削除(d)する

置換の次に便利なのが、不要な行を丸ごと削除する「削除(d)」の機能です。

例えば、設定ファイルやデータファイルから「#」で始まるコメント行や、何もない空行を一括で削除する場合などに使用されます。

まずは、動作確認用として以下のような内容の list.txt というテキストファイルを用意したとします。

# コメント行(行番号: 1)
行番号: 2
行番号: 3

# コメント行(行番号: 4)
行番号: 5

このファイルに対して、上書きをせずに「削除した結果を画面に表示」させる場合は、以下のコマンドを実行します。

# 1. 「#」で始まるコメント行を削除して表示する
sed '/^#/d' list.txt

# 2. 中身が何もない「空行」を削除して表示する
sed '/^$/d' list.txt

ここで使われている d(delete)が削除を実行する命令です。スラッシュに挟まれた条件(例:/^#/)に合致した行が、削除の対象になります(※ ^ は行の先頭、$ は行の末尾を意味する特殊な記号です)。

もし、元のファイルを直接書き換えて「コメント行と空行を同時に削除した状態」で上書き保存したい場合は、-i オプションに加えて -e オプション(複数命令の実行)を組み合わせて以下のように実行します。

# コメント削除と空行削除の2つの命令(-e)を同時に実行し、ファイルを上書き(-i)する
sed -i -e '/^#/d' -e '/^$/d' list.txt

実行後に cat list.txt でファイルの中身を確認すると、以下のように不要なコメントと空行が綺麗に消え去り、必要な行だけが残った状態になります。

行番号: 2
行番号: 3
行番号: 5

実務で役立つ!sedによる設定変更スクリプト例

それでは、実際のスクリプトで sed がどのように使用されるか、具体的な例を見ていきましょう。

このスクリプトは、アプリケーションの設定ファイル(app.conf)の中のポート番号の設定を、スクリプト実行時の引数を使って自動で書き換えるものです。

#!/bin/bash
set -euo pipefail

# app.conf の中身の例:
# PORT=8080
# DB_HOST=localhost

# 引数として新しいポート番号を受け取る(例: ./update_config.sh 9000)
# 第1引数が指定されていなければデフォルトで「空」を代入する。
new_port=${1:-}

# 入力チェックの簡易的なエラー処理
if [ -z "$new_port" ]; then
    echo "エラー: 新しいポート番号を指定してください。" >&2
    exit 1
fi

target_file="app.conf"

# 設定ファイルの PORT=8080 の箇所を PORT=新ポート に自動置換する
# ※ 「PORT=」で始まる行を探して置換を行います
sed -i "s/^PORT=.*/PORT=$new_port/" "$target_file"

echo "設定ファイル $target_file のポート番号を $new_port に変更しました。"

実行結果:

# ./update_config.sh 9000
設定ファイル app.conf のポート番号を 9000 に変更しました。
# cat app.conf 
PORT=9000
DB_HOST=localhost

⚠️ 注意点:変数を展開させたい場合は「ダブルクォーテーション」を使う

sed の置換処理の中に、スクリプト内の変数(例:$new_port)を含めたい場合は、置換命令を囲む引用符をシングルクォーテーション(')ではなく、必ずダブルクォーテーション(")で囲んでください。シングルクォーテーションで囲んでしまうと、変数が展開されずに $new_port という文字そのものがファイルに書き込まれてしまう落とし穴があります。

まとめ

sed コマンドを導入することで、人間が手動でファイルを開いて編集する手間を省き、スクリプトによる安全で正確なシステム環境の自動書き換えが実現します。

非常に多機能なコマンドですが、まずは最も頻出する「文字列の置換(s)」と「不要行の削除(d)」の2つの基本パターンから試してみてください。これだけでも日常的な業務自動化の大部分をカバーできるようになります!

コメント

タイトルとURLをコピーしました