docker runを使って最新のPerlでワンライナー書いてみた

これは [エキサイトホールディング アドベントカレンダー2022](https://qiita.com/advent-calendar/2022/excite-hd) の記事です。

# 概要
ふと、最新のPerlでワンライナーを書きたくなる時がありますよね？
ただ手元の環境のバージョンを上げるのは難しい。。。
そんな時にDockerさえあればDockerを使って最新のPerl環境を作って気軽にワンライナーが書けるよね。
という記事です。

# 前提・準備
```html:index.html
<html>
  <body>
    <ul>
      <li>ぴえん</li>
      <li>ぴえんぴえん</li>
      <li>泣きぴえん</li>
    </ul>
  </body>
</html>
```

* ぴえん → 🥺ぴえん🫶
* 泣きぴえん → 🥹泣きぴえん🫶


このような`index.html`があるときに、上記のように文字列を置換したいとします。
まず最初に現状の表示を確かめるため上記のhtmlをブラウザで表示させてみましょう。

```bash
# index.htmlがあるのと同じディレクトリで実行します
# バックグラウンドでphpのビルドインサーバーを立ち上げます。エキサイトではphpがよく使われているので
# また、あとで止めやすいようにコンテナに名前を付けておきます
# せっかくなので2022/12/02時点で最新バージョンである8.2.0RC7を使います
docker run -d --name tmp-php-server -v $PWD:/app -w /app -p 8080:8080 --rm php:8.2.0RC7-alpine3.17 php -S 0.0.0.0:8080
```

[localhost:8080](http://localhost:8080/) にアクセスして表示を見てみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150459/759c7644-6802-18b5-d426-7b63ea0bf6ae.png)
こんな感じ

# では
下記のワンライナーで文字列を置換し、ファイルを書き換えてみます。
```bash
docker run -v $PWD:/app -w /app --rm perl:5.36.0-slim perl -i.bak -CSD -Mutf8 -ple 's/(?#あとで使うように名前付きでキャプチャしておきます)(?<普通ぴえん>(?#否定後読みで"泣き"が前にこない"ぴえん"を取ります)(?<!泣き)ぴえん)/\N{FACE WITH PLEADING EYES}$+{普通ぴえん}\N{HEART HANDS}/gx; s/(?<泣きぴえん>泣きぴえん)/\N{FACE HOLDING BACK TEARS}$+{泣きぴえん}\N{HEART HANDS}/gx' index.html
```

再度 [localhost:8080](http://localhost:8080/) にアクセスして表示を見る。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150459/00d4148b-de8e-2417-1c26-2f593c0db323.png)
成功しました！

## 上記ワンライナーの解説
* `docker run -v $PWD:/app -w /app --rm perl:5.36.0-slim`
    * `-v $PWD:/app` カレントディレクトリをコンテナ内の`/app`にマウント
    * `-w /app` コンテナ内でのワーキングディレクトリを`/app`に設定
    * `--rm` コマンドが終了したときに自動でコンテナを削除するようにしておく
* `perl -i.bak -CSD -Mutf8 -ple`
    * `-i.bak` ファイルを置き換えする。元のファイルは`.bak`の拡張子がついて取って置ける。
    * `-CSD` 標準入力・出力をUTF8として扱うようにするらしい
    * `-Mutf8` ソースコード内でUTF8を使うのでこの辺を付けとく
    * `-ple` ファイルをループして1行ずつ処理して`$_`を`print`するワンライナーで良く付けるやつ
* `s/(?#あとで使うように名前付きでキャプチャしておきます)(?<普通ぴえん>(?#否定後読みで"泣き"が前にこない"ぴえん"を取ります)(?<!泣き)ぴえん)/\N{FACE WITH PLEADING EYES}$+{普通ぴえん}\N{HEART HANDS}/gx;`
    * `x` フラグを付けることで`(?# ... )`で正規表現内にコメントを書けるので、何をしているか正規表現内にコメントしておきました。
    * `\N{FACE WITH PLEADING EYES}` Perlではこのように`\N{}`でUnicode名を指定することでその文字を書けるようです → [このあたり](https://perldoc.jp/docs/perl/5.16.1/perlreref.pod#ESCAPE32SEQUENCES)
    * `$+{普通ぴえん}` 名前付きでキャプチャした、マッチした部分を使用。

# 最後に
一応わざわざ`docker run`を使ってワンライナーしてるのは
🥹 などの絵文字はUnicode 14で追加されたもので、Perl 5.36からUnicode 14に対応したため`\N{FACE HOLDING BACK TEARS}`等の指定は5.36からでないと使用できません。（という合理的っぽい理由もあります…）

このように最新の環境を気軽に使用できるので、`docker run`でワンライナーを書くのは便利ですね！

---
ちなみに、最後に終わったらphpのビルドインサーバーのコンテナを終了させておきます。
```bash
docker stop $(docker ps -f "name=tmp-php-server" -q)
```

## 検証環境
最後に検証した環境は以下。

Windows 10 WSL2 上のUbuntuです。
Dockerのバージョンは以下の通り。
```bash
$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=20.04
DISTRIB_CODENAME=focal
DISTRIB_DESCRIPTION="Ubuntu 20.04.5 LTS"

$ docker version
Client: Docker Engine - Community
 Version:           20.10.21
 API version:       1.41
 Go version:        go1.18.7
 Git commit:        baeda1f
 Built:             Tue Oct 25 18:02:21 2022
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.21
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.18.7
  Git commit:       3056208
  Built:            Tue Oct 25 18:00:04 2022
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.6.9
  GitCommit:        1c90a442489720eec95342e1789ee8a5e1b9536f
 runc:
  Version:          1.1.4
  GitCommit:        v1.1.4-0-g5fd4c4d
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0
```

## 参考リンク

https://genzouw.com/entry/2019/07/24/143749/1691/

https://qiita.com/ohtsuka1317/items/92c339a65533e1e6c6fc

https://lets-emoji.com/unicode14-emoji/

https://www.compart.com/en/unicode/U+1F97A

https://qiita.com/tohta/items/2ba7ecde5636b38ef1f6


* Perlの正規表現など
https://perldoc.jp/docs/perl/5.36.0/perl5360delta.pod#Unicode3214.032is32supported
https://perldoc.jp/docs/perl/5.36.0/perlreref.pod
https://perldoc.jp/docs/perl/5.36.0/perlre.pod