設定初期値を残しつつ直後に新しい設定を書き込みたい、sedで、ワンライナーで。


sedビギナーの手記

問い1: たとえばこんな設定ファイルがあった

sample.conf
# ServerName gives the name and port that the server uses to identify itself.
# This can often be determined automatically, but we recommend you specify
# it explicitly to prevent problems during startup.
ServerName localhost:80

# DocumentRoot: The directory out of which you will serve your
# documents. By default, all requests are taken from this directory, but
# symbolic links and aliases may be used to point to other locations.
DocumentRoot "/var/www/htdocs"

初期状態を忘れないよう残しつつ、新しい設定を書き込みたい。
言い換えれば、

  • 元の設定をコメントアウトしながら、
  • 次の行に新しい設定を書き込む

そして、こんな処理は自動化のスクリプト中に出てくる処理なので、各書き換えを1行ずつ、つまりなるべくワンライナーでやりたい。
たとえばServerName127.0.0.1:8080にした結果はこんな出力が期待される。

期待される結果
# ServerName gives the name and port that the server uses to identify itself.
# This can often be determined automatically, but we recommend you specify
# it explicitly to prevent problems during startup.
#ServerName localhost:80
ServerName 127.0.0.1:8080

# DocumentRoot: The directory out of which you will serve your
# documents. By default, all requests are taken from this directory, but
# symbolic links and aliases may be used to point to other locations.
DocumentRoot "/var/www/htdocs"

思いついた解

上記3条件を満たすにはこうだろうか。

gsed -i -e "s|\(^ServerName\).*$|#&\n\1 127.0.0.1:8080|" sample.conf

実験環境はMacOSだったが、MacOS標準のsedは振る舞いが異なるので、HomeBrew経由でgnu-sedをインストールした。
&sed 上でマッチした正規表現全体をあらわすそうなので、前半部分をグループ化(= \1)して、
コメント記号と改行、新設定値を組み合わせて出力。
デリミタが/でなく|なのは、そう、書き換える値が/var/www/htdocsみたいなことってよくありますよね。

不満その1

ぱっと見、「どこが設定値?コメント記号はどれ?」とわかりにくい。

不満その2

パターンスペース、ホールドスペースを使ったらもっと綺麗に書けそう。

不満その3

冪等性に欠ける。
二回実行したらこうなってしまう。

期待される結果
# ServerName gives the name and port that the server uses to identify itself.
# This can often be determined automatically, but we recommend you specify
# it explicitly to prevent problems during startup.
#ServerName localhost:80
#ServerName 127.0.0.1:8080
ServerName 127.0.0.1:8080

問い2: たとえばこんな設定ファイルがあった

sample.conf
# ServerName gives the name and port that the server uses to identify itself.
# This can often be determined automatically, but we recommend you specify
# it explicitly to prevent problems during startup.
ServerName localhost:80

# DocumentRoot: The directory out of which you will serve your
# documents. By default, all requests are taken from this directory, but
# symbolic links and aliases may be used to point to other locations.
DocumentRoot "/var/www/htdocs"

この設定ファイルのServerAdminを書き換えたい。

  • 元の設定をコメントアウトしながら、
  • 次の行に新しい設定を書き込む
  • なるべくワンライナー

え、そもそもServerAdminがないって?ありそうなのに?
じゃあ一応探してみて、無かったら文末に追加で良いよ。

期待される結果
# ServerName gives the name and port that the server uses to identify itself.
# This can often be determined automatically, but we recommend you specify
# it explicitly to prevent problems during startup.
ServerName localhost:80

# DocumentRoot: The directory out of which you will serve your
# documents. By default, all requests are taken from this directory, but
# symbolic links and aliases may be used to point to other locations.
DocumentRoot "/var/www/htdocs"
ServerAdmin admin@localhost

思いついた解

gsed -i -e '/^ServerAdmin /{h;s|\(ServerAdmin\) .*|#&\n\1 admin@localhost|};${x;/^$/{s||ServerAdmin admin@localhost|;H};x}' sample.conf

わかりやすくするために、既存の書き換えはold@localhost1、新規の書き込みをnew@localhostとするなら、

gsed -i -e '/^ServerAdmin /{h;s|\(ServerAdmin\) .*|#&\n\1 old@localhost|};${x;/^$/{s||ServerAdmin new@localhost|;H};x}' sample.conf

これを続けて2回実行すると下記の結果が得られる。

sample.conf
# ServerName gives the name and port that the server uses to identify itself.
# This can often be determined automatically, but we recommend you specify
# it explicitly to prevent problems during startup.
ServerName localhost:80

# DocumentRoot: The directory out of which you will serve your
# documents. By default, all requests are taken from this directory, but
# symbolic links and aliases may be used to point to other locations.
DocumentRoot "/var/www/htdocs"
#ServerAdmin new@localhost
ServerAdmin old@localhost

不満その4

パターンスペース使ってもあまり綺麗じゃない、どころか余計わかりにくくなってしまった。

不満その5

DRYじゃない。
ServerAdminって3回も書かなきゃいけないのかい。

不満その6

ここまで来たら、ワンライナー諦めたくなる。

最適解

(文章はここで途切れている…)