2006-12-20

ed を sed 風に使ってファイル内の文字列を置換する

複数のファイルの中身に対して一括置換したい場合、いちいち、ファイルをエディターで開いて置換をするのは面倒。shell スクリプトかワンライナーを書きたい。ぼくはそんな時、sed ではなく ed コマンドを使ってる。

ed のおさらい

ed は、ライン・エディターと呼ばれる種類のエディター。vi や Emacs、秀丸、gedit などは、スクリーン・エディターと呼ばれる。

ed は、- オプション (?) で標準入力からコマンドを受け取るようになる。例えば、fuga.txt 内の foobar に置換する場合を考えやう。次のようにする。ヒア・ドキュメントを使ってコマンドを ed に渡す:

$ ed - fuga.txt <<EOF
%s/foo/bar/g
w
EOF

ed に渡すコマンドは、一行一コマンドで書く。

sed と違う点は二つ。sed はデフォールトで、ファイルの中身全体に置換を行なう。一方、ed は置換を行なう範囲を教えてあげないといけない。例えば、今回の例だと 1s/foo/bar/g とすると一行目に対してのみ置換が行なわれる。1,10s/foo/bar/g なら一行目から十行目まで。1,$s/foo/bar/g なら一行目からファイルの終わりまで置換が実行される。上の例で使った %1,$ と同じ意味を持つ。これが注意の一点目。

二点目は、ed ではファイルを保存しないといけない、ということ。だって、エディターだからね。ファイルを編集した後は、保存しないと! ファイルの保存コマンドは w

ワンライナー で ed を使う

ワンライナーで使う場合は、echo コマンドで ed のコマンドを渡す。

$ echo "%s/foo/bar/g\nw\n" | sed - fuga.txt

シェル・スクリプトで ed を使う

シェル・スクリプトや、ed のコマンドを使い回すような具合は、ed のコマンドを別途ファイルに書き出しとくといいかな。

$ cat hoge.ed
%s/foo/bar/g
w
$ sed - fuga.txt < hoge.txt

つまり、複数ファイルの一括置換はこんな感じ 。

$ for i in *; do ed - "$i" > hoge.txt; done

元ネタ...

このエントリーは、某日記さんのエントリーにインスパイヤーされて書いた。

複数のファイルの特定文字列を一括変換したい場合、

for i in *; do cat < "$i" | sed 's/AAA/BBB/' > "$i"; done

とすると良さそうに見えるけど、これってポータブルなのかねぇ。

某日記(中期) より引用

この sed を使った方法で

for i in *; do sed 's/AAA/BBB/' > "$i" < "$i"; done

が使えないのは、出力先を指定した時点で、その出力先のファイルが新規のファイルとしてオープンされしてしまうからだったかな? その後に「入力」がファイルを開こうとするけど、既に「出力」によって空ファイルに差し換えられてるから、置換に失敗してしまう、と。ぼくはそんな風に理解してる。合ってる?

それで、某日記さんや odz buffer さんは、sed 使ってのを解決しようと頑張ってる。

ぼくも、sed で何とか出来ないものかと思っていた時期がありました。

で... 結局、ファイルを編集したいなら、エディターを使えば早いという結論に落ちついた。

ed のポータビリティー

ed は、vi よりも基本的なコマンドだから、ほとんどの Unix に入ってると思う。FreeBSD にあるかどうか心配だけど、FreeBSD 用の ed の man が Google で見つけられたから、きっと大丈夫でせう。ただ、昔の Cygwin には vi はあっても ed がなかった。あれはショックだった。

最後に、ぼくのメモの中でこんなのを見つけた。

ed には大きなファイルを書き換えられない、という制限がある。大体 100,000 文字以上は扱えないらしい。

出典が不明なんだけど、注意しとかないといけないかも。

1 comment:

  1. echo -en "s/foo/bar/g\nw\n" | ed - fuga.txt

    ReplyDelete