Linuxにおけるファイル内容の置換方法とその応用の手順を先に要約すると「置換は最初にgrepで対象行を数え、sedを上書きなしで実行してdiffを確認し、合格後だけバックアップsuffix付きの-iまたは検証済み一時ファイルへ切り替えます。設定ファイルでは構文検査とサービスのreload可否までを一つの手順にします。」となります。対象環境はGNU sedを例にし、macOSやBSD sedでは-iの引数仕様が異なる点を分ける構成です。
置換前に一致箇所と出現回数を確定する
ファイル置換では、対象ファイルのSHA-256、所有者、権限、置換前文字列の出現件数を先に記録します。grepで対象行を限定し、sedの結果は別ファイルへ出力してdiffで確認します。期待件数と一致した場合だけ原本と入れ替え、異なる場合は生成物を破棄して原本を保持します。
- readlink -fとstatで対象パス、所有者、権限、symlinkを確認する
- grep -nFで置換前文字列の件数と行番号を記録する
- fileとlocaleで文字コードを確認する
- 対象が生成物か正本か、設定管理ツールの管理下かを確認する
candidate fileへ置換してdiffを読む
設定ファイルの一括置換は停止や接続断につながります。対象件数の上限を決め、まず候補ファイルを作ってdiffをレビューし、アプリ固有の構文検査を通します。反映は小さな単位で行い、reload後のhealth checkを確認します。異常ならサービスをむやみに再起動せず、退避した元ファイルをcp -pで戻し、構文検査後に元の運用手順でreloadします。ログの機密情報マスクは正本を改変せず、共有用のコピーへ実施します。
一件・複数file・backup付き反映を選ぶ
固定文字列の対象を確認
grep -nF -- 'old.example.test' app.conf
正規表現が不要なら-Fを使い、パターン先頭がハイフンでもオプション扱いされないよう–で区切ります。0件、想定件数、過剰件数で後続処理を分けます。
標準出力へ置換して差分を見る
sed 's|old\.example\.test|new.example.test|g' app.conf > app.conf.candidate
diff -u -- app.conf app.conf.candidate
元ファイルへ書かず候補を作ります。区切りを|にするとURLのスラッシュを読みやすくできますが、検索側のドットは正規表現なのでエスケープが必要です。
確認後にバックアップ付きで反映
cp -p -- app.conf app.conf.before-20260717
sed -i.bak 's|old\.example\.test|new.example.test|g' app.conf
diff -u -- app.conf.before-20260717 app.conf
GNU sedの-i.bakは元内容をapp.conf.bakへ残します。cp -pの別バックアップも保持し、所有者・mode・mtimeの要件を事前に確認します。
複数ファイルはfindの対象を表示してから
find ./config -type f -name '*.conf' -print
find ./config -type f -name '*.conf' -exec grep -lF -- 'old.example.test' {} +
最初の2行は読み取りだけです。再帰対象と一致ファイルをレビューし、version controlやスナップショットを用意してから限定的な変更を行います。
0回・複数回・読取errorを区別する
sedのsコマンドは既定で各行の最初の一致だけを置換し、gフラグで同じ行の全一致を置換します。アドレス範囲を付ければ特定行だけに絞れます。置換文字列中の&は一致全体を表すため、リテラルの&にはエスケープが必要です。CSVのカンマを単純置換する方法は引用符内カンマを壊すため、構造化データはCSV対応ツールで処理します。
grepの行数と文字列出現数を混同しない
- sed -niを組み合わせて空ファイルにする
- 拡張正規表現を-Eなしで書く
- symlink先を意識せず置換する
- 改行コードや文字コードを変える
- 設定管理が次回配布で変更を巻き戻す
fixtureで置換前後と原本不変を照合する
grep -nFで旧文字列0件、新文字列が想定件数であることを確認し、diff -uのstatus 1を期待する内容差、status 2以上を実行errorとして記録します。sha256sumで変更前後を記録し、構文検査、テスト接続、監視の順で確認します。期待外なら元ファイルを戻し、旧hashへ一致したことを確認してから原因を切り分けます。
old=0・new増加・diff正常を同時に検証する
grep -nFは一致行の位置確認、grep -oFとwc -lは固定文字列の出現数確認に使います。candidateではold_after=0とnew_afterの増分を検証し、diffとcmpはstatus 0・1・2以上を別々に保存します。
同一行の複数出現も数えるfixture
count_fixed() {
file=$1
needle=$2
matches=$(LC_ALL=C grep -oF -- "$needle" "$file")
rc=$?
case $rc in
0) printf '%s\n' "$matches" | wc -l ;;
1) printf '0\n' ;;
*) return "$rc" ;;
esac
}
old_before=$(count_fixed app.conf 'old.example.test') || exit 2
new_before=$(count_fixed app.conf 'new.example.test') || exit 2
sed 's|old\.example\.test|new.example.test|g' app.conf > app.conf.candidate || exit 2
old_after=$(count_fixed app.conf.candidate 'old.example.test') || exit 2
new_after=$(count_fixed app.conf.candidate 'new.example.test') || exit 2
expected_new=$((new_before + old_before))
printf 'old_before=%s old_after=%s new_before=%s new_after=%s expected_new=%s\n' \
"$old_before" "$old_after" "$new_before" "$new_after" "$expected_new"
test "$old_before" -gt 0 && test "$old_after" -eq 0 && test "$new_after" -eq "$expected_new"
置換結果をold/new件数とstatusで判定する
- 置換条件成立:old_beforeが1以上、old_afterが0、new_afterがnew_before+old_beforeに一致する
- 対象文字列なし:old文字列が0件なら既に反映済みか対象path違いとして停止し、空fileを上書きしない
- 生成・diff error:permission、disk不足、sed syntax、diff whitespace errorはnonzeroとして候補fileを破棄する
sed -iの実装差とbackup suffix差を避けるため、候補fileを作る方式を基準にします。正規表現を意図しない置換ではdot等をpatternとして扱わず、固定文字列の確認とescape済みsed式を対応させます。
内容差とcommand errorを別statusにする
diff -u -- app.conf app.conf.candidate > app.conf.candidate.diff
diff_rc=$?
cmp -s -- app.conf app.conf.candidate
cmp_rc=$?
printf 'diff_status=%s cmp_status=%s\n' "$diff_rc" "$cmp_rc"
case "$diff_rc:$cmp_rc" in
1:1) printf '%s\n' 'expected content difference' ;;
0:0) printf '%s\n' 'no replacement occurred' ;;
*) printf '%s\n' 'verification command error' >&2; exit 2 ;;
esac
0件、1件、同一行に複数、read-only file、disk不足を小さなcopyで試します。期待差分以外が無いこと、line数、mode、owner、hashを照合し、反映後もgrepで旧値0件を確認します。
採用条件は、old_beforeが1以上、old_afterが0、new_afterがnew_beforeとold_beforeの合計に一致することです。行数ではなく固定文字列の出現数で照合し、diffとcmpの終了statusも別々に記録します。

コメント