やりたいこと
複数のテキストファイルの中の文字列を別の文字列に一括置換したい。
俺たちは雰囲気でsedを使っている。
sedを20年使ってても(年に2~3回ペースなこともあって)使うのが難しい。何が難しいって、オプションとかフラグとか正規表現とか。そのたびに検索して事例見つけてコピペしている。
よく
$sed -i -e "s/foo/bar/g" file.txt
とかあって、-iオプションは上書き、g修飾子はグローバルマッチ(ファイル内のすべて置換)で、「あぁ、file.txtのなかのfooをbarに全部置き換えて上書きするんだな」ってわかるけど、じゃあ、-eオプションって何?/foo/bar/の前のsって何?必要?って言われるとわからん。俺たちは雰囲気でsedを使っている。
サクッとsedの代わりになるものとかないかなーって【脱sed】いい加減シェルスクリプトで文字列をsedで置換するなんてやめよう #Linux - Qiitaとか、trコマンドとか見てみるけど、なんかイマイチ理解できなかったりファイル上書きできなかったり*1で結局sedに戻ってくることになるので、ちゃんと自分用にメモしておく。
よく使うものだけ
例を挙げながらメモ。file.txtとして
foo bar foo bar baz foo bar baz qux /foo/bar/ {foo} {bar} {baz} @foo@bar@baz@qux@
を用意した。これをさっきの
$sed -i -e "s/foo/bar/g" file.txt
に通すと
bar bar bar bar baz bar bar baz qux /bar/bar/ {bar} {bar} {baz} @bar@bar@baz@qux@
になる。fooが全部barになっている。
-eオプション
【 sed 】コマンド(基礎編その4)――文字列を置き換える/置換した行を出力する:Linux基本コマンドTips(56) - @ITでは
--expression=スクリプト スクリプト(コマンド)を追加する
ってあるけど、スクリプト(コマンド)ってなんやねん?というと、この場合は"s/foo/bar/g"。2つつなげたい場合は-eオプションを使う。同時にbazをzzzにしたい場合は
$sed -i -e "s/foo/bar/g" -e "s/baz/zzz/g" file.txt
で、
bar bar bar bar zzz bar bar zzz qux /bar/bar/ {bar} {bar} {zzz} @bar@bar@zzz@qux@
となる。
なので、スクリプト(コマンド)が1つだけの場合は、-eオプションは不要。
s/foo/bar/のsとはスクリプト(コマンド)
スクリプト(コマンド)とは置換以外にもいろいろある。 例えばaスクリプト(コマンド)は「テキストの追加」を行う。
$sed "a add text" file.txt
とやると、
foo bar add text foo bar baz add text foo bar baz qux add text add text /foo/bar/ add text {foo} {bar} {baz} add text @foo@bar@baz@qux@ add text
とすべての行の次にadd textが加えられる。
sスクリプト(コマンド)は「テキストの置換」になる。
正規表現
sedの正規表現は、POSIXの基本正規表現。簡単な置換ならこれで十分。
fo*でfooもfooooooも置換
$ sed -e "s/@fo*/bar/g" file.txt foo bar foo bar baz foo bar baz qux /foo/bar/ {foo} {bar} {baz} bar@bar@baz@qux@
エスケープのバックスラッシュ
$ sed -e "s/foo\//bar/g" file.txt foo bar foo bar baz foo bar baz qux /barbar/ {foo} {bar} {baz} @foo@bar@baz@qux@
行頭宣言の^
$ sed -e "s/^foo/bar/g" file.txt bar bar bar bar baz bar bar baz qux /foo/bar/ {foo} {bar} {baz} @foo@bar@baz@qux@
拡張したPOSIX正規表現だとエスケープが不要になるみたい。sedで躓くLinux(POSIX)の正規表現|jig.jp engineers
だが、JavaScriptやPerlなどのPCRE 形式の正規表現ではない。
空行を残さないで改行ごと置換する
sedはラインエディタなので行ごとにスクリプト(コマンド)を実行する。なので、1行丸ごと消したい場合に空行が残ってしまう。
$ sed -e "s/foo bar baz qux//" file.txt foo bar foo bar baz /foo/bar/ {foo} {bar} {baz} @foo@bar@baz@qux@
-zオプション
zオプションは改行ごとにスクリプト(コマンド)を実行するのではなく、Null文字ごとにスクリプト(コマンド)を実行する。NULL文字は\0で表現できる。普通のテキストファイル内にそんなものは無い。
改行コードは\n。Windowsだと\r\n
$ sed -z -e "s/foo bar baz qux\n//" file.txt foo bar foo bar baz /foo/bar/ {foo} {bar} {baz} @foo@bar@baz@qux@
…が、zオプションは2018年にリリースされたversion4.2.2で導入されたらしく、むしろまだsedに新機能入るのか!!!という驚きの方があるが、手元のCentOS6では動かなかった。そんな古いOS使うなって。
$ sed --version GNU sed 4.2.1版 Copyright (C) 2009 Free Software Foundation, Inc.