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

# `sed`ビギナーの手記

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

```conf: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行ずつ、つまり**なるべくワンライナー**でやりたい。
たとえば`ServerName`を`127.0.0.1:8080`にした結果はこんな出力が期待される。

```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
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

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

```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
#ServerName 127.0.0.1:8080
ServerName 127.0.0.1:8080
```

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

```conf: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`がないって？ありそうなのに？
じゃあ一応探してみて、**無かったら文末に追加**で良いよ。

```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 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回実行すると下記の結果が得られる。

```conf: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

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

## 最適解

（文章はここで途切れている…）