コマンドラインで使えるperlワンライナー

# 0. はじめに
本稿は[Wanoグループ Advent Calendar 2020](https://qiita.com/advent-calendar/2020/wano-group) 14日目の投稿になります。

 ラクダ本[^1]の緒言にもあるようにPerlは"簡単な仕事を簡単にできるように設計されている"ので、日々のちょっとした作業、特にちょっとしたテキストファイルの処理をしたりする際に何かとお世話になっています。また、自分は怠惰なので、その場限りのちょっとした作業のためにスクリプトファイルを書くことをめんどくさがります。PerlはAwkのように、短い記述量で多くのことをこなしてくれるため、ワンライナーをパイプ処理の途中にねじ込むことも容易です。本稿では、そんな日々のちょっとした作業を楽にしてくれるかもしれないコマンドライン上でのPerlのスニペットやワンライナーを紹介します。

# 1. 普段使いのUnixコマンドのレパートリーにPerlを加える
多くのUnix (もしくは、Linux)環境において、Perlは標準で入っているので、特別なセットアップ作業等の必要はありません。最初からperlコマンドが使える環境がほとんどです。標準外のCPAN Moduleが使うようなことが無いのであれば、何も特別な準備は必要ないでしょう。尤も、CPAN Moduleを使わないのであれば、awkやsedで事足りる場合が多いので、よりPerlで怠惰に作業をこなすために自由にCPAN Moduleを導入・管理できる環境を軽く整えておくのがよいでしょう。とはいえ、これも、標準のcpanコマンドよりちょっと便利なcpanminus[^2]をインストールして、ホーム直下にでもcpanfileを置いて、それでモジュールを管理する程度で十分でしょう (nodejsにおけるnpmとglobalのpackage.jsonみたいなものですね)。
　また、本稿ではperlの文法や起動オプションについては特に解説してないので、不明点については、perlのドキュメント[^3]や本稿末尾に記載の書籍[^4]<sup>,</sup>[^5]</sup>等を適宜参照してください。

# 2. Perlによるテキスト処理あれこれ
今回はサンプルとしてSpotify Charts[^6]からダウンロードできるCSVファイルを使用します。とりあえず、2020/12/01付けの日本のTOP 200チャートのデータを取得して、中身を確認してみます。

```sh:download
$ curl -s https://spotifycharts.com/regional/jp/daily/2020-12-01/download -o spotify.top200.daily.jp.20201201.csv
$ head spotify.top200.daily.jp.20201201.csv
```

中身(headの出力)はこんな感じ

```txt
,,,"Note that these figures are generated using a formula that protects against any artificial inflation of chart positions.",
Position,"Track Name",Artist,Streams,URL
1,Dynamite,BTS,309097,https://open.spotify.com/track/4saklk6nie3yiGePpBwUoc
2,炎,LiSA,301412,https://open.spotify.com/track/0cSkn2l67csUljEy0EEBPn
3,夜に駆ける,YOASOBI,247609,https://open.spotify.com/track/3dPtXHP0oXQ4HCWHsOA9js
4,ドライフラワー,優里,221340,https://open.spotify.com/track/7dH0dpi751EoguDDg3xx6J
5,虹,"Masaki Suda",211879,https://open.spotify.com/track/7AIj86wFWqm7X1TZ2hzHwS
6,"Stand by me, Stand by you.",HIRAIDAI,211637,https://open.spotify.com/track/2RbVD1IgXtyQFlLtbthaZ3
7,"Step and a step",NiziU,202135,https://open.spotify.com/track/5DgAgJbHcm74RyA9YKj6k1
8,"Make you happy",NiziU,177733,https://open.spotify.com/track/4FrxgrNbNrdfq8093JaEJf
```

左詰めで見にくいので、perlを使って見やすい感じに整形しましょう。エクセルで開けばいいのでは？と思うかもしれませんが、自分は怠惰なのでターミナルから離れることすらめんどうなのです。後は、結果を` | vim -`でvimに渡して、そのままごにょごにょできるのでコマンドライン上でやる方が何かと便利だったりします。

```display_chart.sh
tail -n +2 spotify.top200.daily.jp.20201201.csv \
| head \
| perl -CS -MText::CSV=csv -E '$,="\t";@csv = @{csv(in => *STDIN)};map { say @{$_} } @csv' \
| column -tn -s $'\t'
```
```display_chart.outoput
Position  Track Name                  Artist       Streams  URL
1         Dynamite                    BTS          309097   https://open.spotify.com/track/4saklk6nie3yiGePpBwUoc
2         炎                          LiSA         301412   https://open.spotify.com/track/0cSkn2l67csUljEy0EEBPn
3         夜に駆ける                  YOASOBI      247609   https://open.spotify.com/track/3dPtXHP0oXQ4HCWHsOA9js
4         ドライフラワー              優里         221340   https://open.spotify.com/track/7dH0dpi751EoguDDg3xx6J
5         虹                          Masaki Suda  211879   https://open.spotify.com/track/7AIj86wFWqm7X1TZ2hzHwS
6         Stand by me, Stand by you.  HIRAIDAI     211637   https://open.spotify.com/track/2RbVD1IgXtyQFlLtbthaZ3
7         Step and a step             NiziU        202135   https://open.spotify.com/track/5DgAgJbHcm74RyA9YKj6k1
8         Make you happy              NiziU        177733   https://open.spotify.com/track/4FrxgrNbNrdfq8093JaEJf
9         裸の心                      Aimyon       176070   https://open.spotify.com/track/4Jv7U0JJpbQnOrjtDwDZTZ
```
見やすい。csvからtsvへの変換は、`sed -E 's/("([^"]*)")?,/\2\t/g'`のような正規表現でもいいのですが、自分はいちいち覚えてられないので、Text::CSV Moudle[^7]を使ってます。ワンライナーでも使いやいのでお勧めのMoudleです。テーブル整形の部分は、個人的に`column`コマンドが好きなのでこれを使っていますが、Text::Table::Tiny[^8]みたいなModuleを使ってもいいかもしれないですね。

　続いて、チャートインの多いアーティストとその合計Stream数をさくっと確認してみましょう。

```artits2chart_in_count_with_sum_stream.sh
tail -n +2 spotify.top200.daily.jp.20201201.csv \
| perl -CS -MText::CSV=csv -E '$,="\t";@csv = @{csv(in => *STDIN)}; map { say @{$_} } @csv' \
| perl -CS -F'\t' -alE '$h{$F[2]}++; $j{$F[2]}+=$F[3]; END { $,="\t"; map { say $_, $h{$_}, $j{$_} } keys %h }' \
| sort -t $'\t' -k2 -k3 -nr \
| column -nt -s $'\t' \
| head -n 20
```
```artits2chart_in_count_with_sum_stream
BTS                     14          1024687
Official HIGE DANdism   9           925246
back number             9           395498
King Gnu                7           450881
Mrs. GREEN APPLE        6           475984
Kenshi Yonezu           6           360720
TWICE                   6           307827
YOASOBI                 5           647537
NiziU                   5           471590
Aimyon                  5           449613
HIRAIDAI                5           346069
Masaki Suda             4           376490
ヨルシカ                4           264116
ONE OK ROCK             4           121369
ARASHI                  4           117007
LiSA                    3           525058
もさを。                3           189626
Vaundy                  3           181857
BLACKPINK               3           113619
Ariana Grande           3           96995
```
少々タイプ数が多いですが、慣れればこのくらいまでは思考停止で書けるようになると思います。LiSAがチャートイン楽曲数3つで、チャートイン楽曲数7のKing GnuのStream合計を超えてるのすごいですね（感想）。

　後は、あまりやらないですが、CSVをJSONに変換してみます。

```csv2json.sh
tail -n +2 spotify.top200.daily.jp.20201201.csv \
| perl -CI -MText::CSV=csv -MJSON::PP=encode_json -E '$,="\t";@csv = @{csv(in => *STDIN, headers => "auto")}; say encode_json([@csv[0..2]]);' \
| jq .

```
```csv2json.output.json
[
  {
    "Artist": "BTS",
    "Streams": "309097",
    "URL": "https://open.spotify.com/track/4saklk6nie3yiGePpBwUoc",
    "Position": "1",
    "Track Name": "Dynamite"
  },
  {
    "Streams": "301412",
    "Artist": "LiSA",
    "URL": "https://open.spotify.com/track/0cSkn2l67csUljEy0EEBPn",
    "Position": "2",
    "Track Name": "炎"
  },
  {
    "Position": "3",
    "URL": "https://open.spotify.com/track/3dPtXHP0oXQ4HCWHsOA9js",
    "Streams": "247609",
    "Artist": "YOASOBI",
    "Track Name": "夜に駆ける"
  }
]
```
　CSVをJSONに変換したいということはあまりないかもしれませんが、逆に、JSONをテーブル形式で見たいことは結構あるので、以下のようなワンライナーはよく使います。

```json2table_view.sh
bash ./csv2json.sh > csv2json.output.json
cat csv2json.output.json \
| perl -CO -MJSON::PP=decode_json -E '$json = join("", <>); @h = ("Track Name", "URL"); @json = @{decode_json($json)}; $,="\t"; say @h; map { say @$_{@h} } @json' \
| column -tn -s $'\t'
```

```
Track Name                  URL
Dynamite                    https://open.spotify.com/track/4saklk6nie3yiGePpBwUoc
炎                          https://open.spotify.com/track/0cSkn2l67csUljEy0EEBPn
夜に駆ける                  https://open.spotify.com/track/3dPtXHP0oXQ4HCWHsOA9js
ドライフラワー              https://open.spotify.com/track/7dH0dpi751EoguDDg3xx6J
虹                          https://open.spotify.com/track/7AIj86wFWqm7X1TZ2hzHwS
Stand by me, Stand by you.  https://open.spotify.com/track/2RbVD1IgXtyQFlLtbthaZ3
Step and a step             https://open.spotify.com/track/5DgAgJbHcm74RyA9YKj6k1
Make you happy              https://open.spotify.com/track/4FrxgrNbNrdfq8093JaEJf
裸の心                      https://open.spotify.com/track/4Jv7U0JJpbQnOrjtDwDZTZ
紅蓮華                      https://open.spotify.com/track/0qMip0B2D4ePEjBJvAtYre
```
実際には、jqでざっくりフィルタリングしてからPerlに突っ込むことが多いですね。JSON Moduleを使うときは内部文字コード変換が入るので、`-C`オプションの値に注意が必要です。

# 3. おわりに
PerlやPythonのように大体の環境に標準で入ってるスクリプト言語をアーミーナイフとして使いこなせると何かと便利です。Perlは書いたことあるけど、コマンドラインドラインでは使わないという人は、せっかくなので、ワンライナーPerlについて色々と調べてみるとシェルでの生活がより良いものなるかもしれません。

[^1]: L. Wall, T. Christiansen, J. Orwant: *Programming Perl (Third Edition)*, O'Reill Media, Inc., 2000 (近藤嘉雪・訳: プログラミングPerl 第3版 Volume 1, O'Reilly Japan, 2002).
[^2]: App::cpanminus, meta::cpan, Accessed 13 Dec. 2020 [[Link]](https://metacpan.org/pod/App::cpanminus)
[^3]: `perldoc perlrun` 
[^4]: T. Maher: *Minimal Perl: For Unix and Linux People*, Manning Publication, 2006 (安藤慶一, 磯部孝一郎・訳: ミニマルPerl, O'Reilly Japan, 2008).
[^5]: S. Agarwal: *Perl one-lines cookbook* [OnlineBook], 2020, Accessed 13 Dec. 2020. [[Link]](https://learnbyexample.github.io/learn_perl_oneliners/cover.html).
[^6]: Spotify AB: *Spotify Charts*, Accessed 13 Dec. 2020 [[Link](https://spotifycharts.com/)].
[^7]: Text::CSV, meta::cpan, Accessed 13 Dec. 2020 [[Link]](https://metacpan.org/pod/Text::CSV)
[^8]: Text::Table::Tiny, meta::cpan, Accessed 13 Dec. 2020 [[Link]](https://metacpan.org/pod/Text::Table::Tiny)