LispをやるひとはLisper、RをやるひとはRおじさんというらしいですが、じゃあSedをやるひとは?
Sederというらしいです。
一人前のSederになるために、Sedの細かい話をします。うそです。あんまり細かくないですけど、たぶんこれですら伝わらない予感…
Sedの基本文法
Sedの文法は単純明快です。
アドレス コマンド
という順番になっています。
コマンドはs
とかp
とかアルファベット一文字です。引数の形は様々です。
アドレスというのは、「この正規表現に引っかかる行」とか「何行目から何行目」とかいう条件です。アドレスにあてはまる行にだけコマンドが実行されます。アドレスは省略可能で、省略した場合はかならず実行されます(AWKと同じノリですね)。コマンドによって使うアドレスのタイプが違ったりするみたいですが、その辺には踏み込まないことにします(あんまりよく知らない)。
たとえばさっきの
s/hoge/fuga/
とかいうのは、アドレス→省略、コマンド→s
、です。残りはコマンドの引数です。とか言うと「えっ、/hoge/
がアドレスじゃないの??」とかいう反応がありそうですが、そこはただのコマンドの引数です。
アドレスを使ってみる
たとえば、こういうファイルがあるとします。
user: yutani admin: true expired: true
これのadmin
だけfalse
にしたいときはどうしますか?
sed 's/^admin: true$/admin: false/' test.yml
とかやってませんか。これだと長いし、空白の数が変わったりすると動かなくなります。じゃあ空白は正規表現にするかーとか言い出すと、途端にややこしくなります。
sed 's/^admin:\( \+\)true$/admin:\1false/' test.yml
読みづらい。
こういうときはアドレスを使って書くとすっきりします。(感じ方に個人差はあるかもしれませんけど...)
sed '/admin/ s/true/false/' test.yml
admin
がある行の、true
をfalse
に置き換えます。(アドレスとコマンドの間の空白はあってもなくてもどちらでも)
あと、{}
で括って複数のコマンドを実行することもできます。
sed '/admin/{s/admin/Administator/; s/true/false/}' test.yml
s
以外のコマンドも使ってみる
個人的な感覚としては、世の中の9割の人はs
コマンドしか知らないんじゃないかという気がしますが、Sedにはいろいろコマンドがあります。いくつか使ってみます。
d
: 行を削除
これはアドレスと組み合わせて使います。たとえばadmin
の行だけ削除したいときは
sed '/admin/ d' test.yml
とかやります。これだけならgrep -v
といっしょじゃん、という話ですが、アドレスは正規表現マッチ以外もいろいろあって、例えば行数を指定して操作したいときとかは便利です。以下は1行目を消す例です。
sed '1 d' test.yml
c
: 行を置換
アドレスと組み合わせて使います。指定した文字列で行を丸ごと置き換えます。たとえば、admin
が含まれる行を# (deleted)
という文字列と置き換えるのはこんな感じです。
sed '/admin/c # (deleted)' test.yml
a
,i
: 行を追加
これもアドレスと組み合わせて使います。a
はそのアドレスの後に指定した文字列の行を追加します。i
はアドレスの前に追加します。
ちょっと分かりづらいと思うので実行結果も添えておくと、↓それぞれこんな感じになります。
$ sed '/admin/a I AM HERE' test.yml user: yutani admin: true I AM HERE expired: true $ sed '/admin/i I AM HERE' test.yml user: yutani I AM HERE admin: true expired: true
n
: 次の行に進む
これはちょっと説明が難しいです。正直、正しい使い方を理解できていません。。
これは例えば、以下のようなJSONファイルでparam2
のvalue
だけを書き換えたい、みたいな、処理が複数行にまたがるケースを考えましょう。(注:ここまでくるとたぶんSedでは荷が重いです。Pythonなりでスクリプトを書くことを検討しましょう)
{ "param1": { "value": 1, "name": "foo" }, "param2": { "value": 23, "name": "bar" } }
そんなときは、「param2
の次の行を読み込んで、数字を0
に置き換える」という感じで書けます。
sed '/param2/{n; s/[0-9]\+/0/}' test.json
ほんとは/value/
とかのアドレスを指定したいですが、{}
の中にさらにアドレスを書いたりはできないのでこうなっています。正直あんまりきれいなやり方じゃないです。
あと、私のようなゆるふわSederではカラテが足りないんですが、ラベルという概念を使うとこんな風にも書けます。
sed '/param2/{n; b cr}; b; :cr; /value/ s/[0-9]\+/0/' test.json
が、ここまでくるともう誰にも(半年後の自分も含む)読めないので、こんなコマンドを書くのはよっぽどのことがない限り避けましょう。これがどういう意味か興味がある方は、以下のSOを読むとわかるかもしれません。
s
で正規表現を指定するときの豆知識
を書こうと思ったんですが、ひとつしか思いつきませんでした。しかもみんな知ってそう。
正規表現のパターンに/
が含まれるとき
たとえばこういうファイルで、misc/work
をworkdir
に置き換えたいとします。このとき、/
が正規表現の区切り文字とかぶるので少し対処が必要です。
[workspace] dir = /home/yutani/misc/work mode = 0755 create = yes
ひとつは\
でエスケープするという方法があります。
sed 's/misc\/work/workdir/' test.ini
もうひとつは、区切り文字を変える方法です。s
コマンドの後に指定した任意の文字が区切り文字になります。なんでもいいんですけど,
とかをよく見る気がします。
sed 's,misc/work,workdir,' test.ini
アドレスも同じく区切り文字を変えることができます。\
の後に指定した任意の文字が区切り文字になります。
sed '\,misc/work,d' test.ini
まとめ
sedは奥が深いのであんまり深入りしないようにしましょう!(あれっ...?)