まえがき
- Nimで自分用の簡易なCLIツールを作って普段使いしている話を書く
なぜNimで作ったか
選定理由は以下のとおりです。
- ネイティブバイナリを出力できる
- 高速
- 書きやすい
- 読みやすい
- めちゃくちゃ少ないコード量で目的を達成できる
- 静的型付け言語なのでコンパイル時に型チェックができる
- テストしやすい
成果物
インストール
あまりにもオレオレすぎたのでNimのパッケージに追加のPRはだしてません。
nimble install
にURLを指定してやればNimのパッケージに存在しなくてもインストールできます。
nimble install https://github.com/jiro4989/yourutils
作ったコマンド
12個あります。それぞれは非常に単純で簡素なコマンドです。
flat
複数行の標準入力を任意の列数にする。
-d
オプションで区切り文字を指定できる。
$ seq 5 | flat
1 2 3 4 5
$ seq 5 | flat -n 2
1 2
3 4
5
$ seq 5 | flat -d ,
1,2,3,4,5
rep
任意の回数文字を連続して出力する。
$ rep 5 A
AAAAA
$ echo 'A B' | rep 5 -i
A BA BA BA BA B
$ rep 1 3 5 A
A
AAA
AAAAA
$ rep $(seq 5) A
A
AA
AAA
AAAA
AAAAA
ucut
cutのunicode対応版。オプションは必要最低限。
マルチバイト文字で入力を区切って取り出せる。
$ echo 1あ2あ3 | ucut -d あ -f 1,2
1 2
-f
のフィールドは何個同じやつを設定しても良いようにしてます。
$ echo "田中,男,20歳,野球,東京都,なし" | ucut -d , -D " " -f 1,2,2,3,2,1,4
田中 男 男 20歳 男 田中 野球
codepoint
文字のコードポイントを出力する。
$ echo hello world | codepoint
char code_point code_point(hex)
h 104 \U68
e 101 \U65
l 108 \U6C
l 108 \U6C
o 111 \U6F
32 \U20
w 119 \U77
o 111 \U6F
r 114 \U72
l 108 \U6C
d 100 \U64
align
テキストの位置を左、中央、右に揃えるコマンド。
マルチバイト文字の表示上の文字幅の違いにも対応している。
% echo $'123\nあいう\nえお' | align right
123
あいう
えお
% echo $'1234\nああああああ\nうえお' | align center -p =
====1234====
ああああああ
===うえお===
aggr
CSVなどの列データを集計して件数、最小値、最大値、合計、平均値、中央値、95パーセンタイル値を計算する。
-f
で何列目のデータを集計するかも指定できます。
$ seq 100 | aggr -D ,
file_name,field,count,min,max,total,avg,median,95percentile
stdin,0,100,1,100,5050,50.5,51,96
subnet
IPアドレスとCIDRを引数に渡すとそのサブネットマスクを出力する。
$ subnet 100.100.200.1/24
100.100.200.1 24 01100100011001001100100000000001 11111111111111111111111100000000
Bashのブレース展開と組み合わせて出力したり。
$ subnet 100.100.200.1/{1..32}
ip_address cidr bin mask
100.100.200.1 1 01100100011001001100100000000001 10000000000000000000000000000000
100.100.200.1 2 01100100011001001100100000000001 11000000000000000000000000000000
100.100.200.1 3 01100100011001001100100000000001 11100000000000000000000000000000
100.100.200.1 4 01100100011001001100100000000001 11110000000000000000000000000000
100.100.200.1 5 01100100011001001100100000000001 11111000000000000000000000000000
100.100.200.1 6 01100100011001001100100000000001 11111100000000000000000000000000
100.100.200.1 7 01100100011001001100100000000001 11111110000000000000000000000000
100.100.200.1 8 01100100011001001100100000000001 11111111000000000000000000000000
100.100.200.1 9 01100100011001001100100000000001 11111111100000000000000000000000
100.100.200.1 10 01100100011001001100100000000001 11111111110000000000000000000000
100.100.200.1 11 01100100011001001100100000000001 11111111111000000000000000000000
100.100.200.1 12 01100100011001001100100000000001 11111111111100000000000000000000
100.100.200.1 13 01100100011001001100100000000001 11111111111110000000000000000000
100.100.200.1 14 01100100011001001100100000000001 11111111111111000000000000000000
100.100.200.1 15 01100100011001001100100000000001 11111111111111100000000000000000
100.100.200.1 16 01100100011001001100100000000001 11111111111111110000000000000000
100.100.200.1 17 01100100011001001100100000000001 11111111111111111000000000000000
100.100.200.1 18 01100100011001001100100000000001 11111111111111111100000000000000
100.100.200.1 19 01100100011001001100100000000001 11111111111111111110000000000000
100.100.200.1 20 01100100011001001100100000000001 11111111111111111111000000000000
100.100.200.1 21 01100100011001001100100000000001 11111111111111111111100000000000
100.100.200.1 22 01100100011001001100100000000001 11111111111111111111110000000000
100.100.200.1 23 01100100011001001100100000000001 11111111111111111111111000000000
100.100.200.1 24 01100100011001001100100000000001 11111111111111111111111100000000
100.100.200.1 25 01100100011001001100100000000001 11111111111111111111111110000000
100.100.200.1 26 01100100011001001100100000000001 11111111111111111111111111000000
100.100.200.1 27 01100100011001001100100000000001 11111111111111111111111111100000
100.100.200.1 28 01100100011001001100100000000001 11111111111111111111111111110000
100.100.200.1 29 01100100011001001100100000000001 11111111111111111111111111111000
100.100.200.1 30 01100100011001001100100000000001 11111111111111111111111111111100
100.100.200.1 31 01100100011001001100100000000001 11111111111111111111111111111110
100.100.200.1 32 01100100011001001100100000000001 11111111111111111111111111111111
IPアドレス部に関してはIPのレンジを指定したりできる。
# 1-10 で1から10の値を全部出力
$ subnet 100.100.200.1-10/24
ip_address cidr bin mask
100.100.200.1 24 01100100011001001100100000000001 11111111111111111111111100000000
100.100.200.2 24 01100100011001001100100000000010 11111111111111111111111100000000
100.100.200.3 24 01100100011001001100100000000011 11111111111111111111111100000000
100.100.200.4 24 01100100011001001100100000000100 11111111111111111111111100000000
100.100.200.5 24 01100100011001001100100000000101 11111111111111111111111100000000
100.100.200.6 24 01100100011001001100100000000110 11111111111111111111111100000000
100.100.200.7 24 01100100011001001100100000000111 11111111111111111111111100000000
100.100.200.8 24 01100100011001001100100000001000 11111111111111111111111100000000
100.100.200.9 24 01100100011001001100100000001001 11111111111111111111111100000000
100.100.200.10 24 01100100011001001100100000001010 11111111111111111111111100000000
-C
で出力に色を付けられる。
$ subnet -C 100.100.200.1/{16..32}
tb
タブ区切りの入力を任意のテーブル形式で出力する。
$ paste <(seq 5) <(seq 6 10) <(seq 11 15)
1 6 11
2 7 12
3 8 13
4 9 14
5 10 15
$ paste <(seq 5) <(seq 6 10) <(seq 11 15) | tb
|1|6|11|
|:---:|:---:|:---:|
|2|7|12|
|3|8|13|
|4|9|14|
|5|10|15|
HTML形式で出力。
$ paste <(seq 5) <(seq 6 10) <(seq 11 15) | tb -f html
<table>
<thead>
<tr><td>1</td><td>6</td><td>11</td></tr>
</thead>
<tbody>
<tr><td>2</td><td>7</td><td>12</td></tr>
<tr><td>3</td><td>8</td><td>13</td></tr>
<tr><td>4</td><td>9</td><td>14</td></tr>
<tr><td>5</td><td>10</td><td>15</td></tr>
</tbody>
</table>
AsciiDoc形式で出力。
$ paste <(seq 5) <(seq 6 10) <(seq 11 15) | tb -f adoc
[options="header"]
|=================
|1|6|11
|2|7|12
|3|8|13
|4|9|14
|5|10|15
|=================
renames
ディレクトリ配下の全てのディレクトリとファイル名を再帰的に変更する。
任意の文字を消したり、別の文字に置き換えたり、小文字にしたり大文字にしたりできる。
どのファイルが更新されるかのDry runもできる。
AmazonMusicで音楽を購入してDLしたときにファイル名に半角スペースやら全角スペースやらクオートやら記号やらが含まれていて血管がブチギレそうになったときに作った。
ファイル名から空白文字(半角スペース、全角スペース、タブ文字)を削除する例。
(deleteはデフォルトで上記文字が削除対象)
-d
はDry runの指定です。リネームはしません。
任意の文字を別の文字に置換する例。
-F
は変更対象のみ出力する指定。
zshprompt
Zshのプロンプト。直前のコマンドの成功の可否でプロンプトの表情が変わる。
あと時刻によって色が変わったりする。
tiff
時刻と時刻の差を求める。
画像のTIFFと勘違いされる可能性があるので名前変えたいけれどイイカンジの名前が思いうかばない。
$ tiff 19:00 18:00
3600 seconds
$ tiff 19:00 18:00 -H
1 hours
$ tiff 19:00 18:00 -M
60 minutes
jsonfmt
標準入力から読み込んだJSONを整形して出力する。
内部的には標準ライブラリを使ってjson.pretty
してるだけ。
$ echo '{"a":1, "b":true, "c":[1, 2, 3], "d":{"a":1, "b":"test"}}' | jsonfmt
{
"a": 1,
"b": true,
"c": [
1,
2,
3
],
"d": {
"a": 1,
"b": "test"
}
}
コード量
コード量の一覧です。コメントとかも含まれてますが。
$ paste <(wc -l src/*.nim) <(wc -l tests/*.nim)
63 src/aggr.nim 14 tests/tflat.nim
79 src/align.nim 208 tests/trenames.nim
36 src/codepoint.nim 23 tests/trep.nim
40 src/flat.nim 79 tests/tsubnet.nim
18 src/jsonfmt.nim 10 tests/tucut.nim
160 src/renames.nim 334 合計
41 src/rep.nim
148 src/subnet.nim
85 src/tb.nim
58 src/tiff.nim
34 src/ucut.nim
37 src/zshprompt.nim
799 合計
エラーハンドリングを必要最低限しかやってないのもありますが、めちゃくちゃコード量が少ないです。
とにかく楽に実装できたことを覚えています。
楽に実装できた理由は以下の3つだと思っています
- Nimの言語仕様
- 標準ライブラリが充実している
-
NimのCLIツール作成用ライブラリcligenがとても便利
- cligenをフル活用した
感想
Nimは書いてて楽しい!
まとめ
以下のことについて書きました。
- 作ったツールの紹介
- NimはCLIツールを楽に実装できる
- Nimは書いてて楽しい
ネイティブバイナリを出力できるNimです。
作ったバイナリを誰かに配布したりできます。
小さなツールの作成からまずはNimに触れてみてほしいです。
以上です。