Getopt::EX
greple コマンドからオプションハンドリングの部分を切り出して、Getopt::EX という Perl モジュールとしてリリースした。
これは、コマンドの実行前にオプションを処理して、新しいオプションや機能を実現するためのものだ。greple を本格的に使おうとすると、オプションの指定がかなり複雑になり、端末から入力するのは現実的ではない。シェルスクリプトにするという手はあるが、管理がしにくい。Getopt::EX を使うと、rc ファイルとモジュールという形でまとめることができる。
optex コマンド
optex は、この Getopt::EX の機能をすべてのコマンドで使えるようにしようというものだ。getoptex
だと長いので optex
にした。読み方には特にこだわっていないが、何となく自分では「オプテック」と呼んでいる。
シェルのエイリアス機能でも近いことはできるが、コマンドそのものを拡張したように見えるので、シェルスクリプトや他のコマンドから間接的に実行される場合にも有効になる点が決定的に違う。
使い方は、optex の引数としてコマンドを指定する方法と:
$ optex date
シンボリックリンクを使う方法がある:
$ ls -l ~/.optex.d/bin/date
~/.optex.d/bin/date -> ~/perl5/bin/optex*
$ date
リンクの置き場所はどこでも構わないが、デフォルトで ~/.optex.d/bin
というディレクトリを想定して、リンクを管理する機能も用意してある。--ln
オプションでリンクを作成し、--rm
オプションで削除することができる。
$ optex --ln date
$ optex --ls -l date
date -> ~/perl5/bin/optex
$ optex --rm date
~/.optex.d/bin/date removed.
$ optex --ls -l date
date: not exist
設定ファイルは ~/.optex.d/ というディレクトリに置く。date
コマンドであれば ~/.optex.d/date.rc
だ。
設定ファイルの中では、次のようなことができる。
option
例えば macOS の date
コマンドには -I[TIMESPEC]
オプションが実装されていないが、次のような設定を ~/.optex.d/date.rc
ファイルに入れておくことで実現できる。
option -I -Idate
option -Idate +%F
option -Iseconds +%FT%T%z
option -Iminutes +%FT%H:%M%z
option -Ihours +%FT%H%z
option --iso-8601 -I
option --iso-8601=date -Idate
option --iso-8601=seconds -Iseconds
option --iso-8601=minutes -Iminutes
option --iso-8601=hours -Ihours
こうすると、次のように実行できる。
% optex date -Iseconds
シンボリックリンクがあれば、普通のコマンドと同じように実行できる。
% date -Iseconds
define
複雑なオプションを定義するためにマクロを使うこともできる。ファイル ~/.optex.d/awk.rc
に次の内容を入れておくと、awk で --vowels というオプションが使えるようになる。
define __delete__ /[bcdfgkmnpsrtvwyz]e( |$)/
define __match__ /ey|y[aeiou]*|[aeiou]+/
define __count_vowels__ <<EOS
{
s = tolower($0);
gsub(__delete__, " ", s);
for (count=0; match(s, __match__); count++) {
s=substr(s, RSTART + RLENGTH);
}
print count " " $0;
}
EOS
option --vowels __count_vowels__
これは、英単語の音節を調べようとしているもので、こんな風に使う。
% awk --vowels /usr/share/dict/words
これは、この記事で使ったスクリプトだ。
https://qiita.com/kaz-utashiro/items/9027eba0c1347d441bd9
expand
複雑なオプションを定義するためには expand を使う。expand は option と同じように使えるが、そのファイルの中でだけ有効になる。find コマンドのオプションは指定するのが面倒なので、こんな風にまとめておけると便利だ。
expand repository ( -name .git -o -name .svn -o -name RCS )
expand no_dots ! -name .*
expand no_version ! -name *,v
expand no_backup ! -name *~
expand no_image ! -iname *.jpg ! -iname *.jpeg \
! -iname *.gif ! -iname *.png
expand no_archive ! -iname *.tar ! -iname *.tbz ! -iname *.tgz
expand no_pdf ! -iname *.pdf
option --clean \
repository -prune -o \
-type f \
no_dots \
no_version no_backup \
no_image \
no_archive \
no_pdf
% find . --clean -print
モジュール
optex はモジュールで拡張することができる。date
コマンドの場合、モジュールは ~/.optex.d/date/
からロードする。ここに ~/.optex.d/date/default.pm
というモジュールがあると自動的に実行される。
モジュールファイルには rc ファイルと同じ内容も書くことができて、この場合別に Perl のコードを書く必要はない。先の awk の例なら ~/.optex.d/awk/vowels.pm
というファイルに以下の内容を入れておくと awk -Mvowels
で --vowels
オプションが有効になる。最初の3行のおまじないは必要だが、その後は rc ファイルと同じだ。
package vowels;
1;
__DATA__
define __delete__ /[bcdfgkmnpsrtvwyz]e( |$)/
define __match__ /ey|y[aeiou]*|[aeiou]+/
define __count_vowels__ <<EOS
{
s = tolower($0);
gsub(__delete__, " ", s);
for (count=0; match(s, __match__); count++) {
s=substr(s, RSTART + RLENGTH);
}
print count " " $0;
}
EOS
option --vowels __count_vowels__
help --vowels count vowels
これは、普通の Perl モジュールなので、何でも書くことができる。
例えば、
package default;
$ENV{LANG} = 'C';
1;
こんな default モジュールを用意して置くと、date
コマンドを実行する前に、環境変数 LANG
を C
に設定する。なので、こんな風になる。
% /bin/date
2017年 10月22日 日曜日 18時00分00秒 JST
% date
Sun Oct 22 18:00:00 JST 2017
その他のモジュールは -M
オプションで指定する。
~/.optex.d/es.pm
というモジュールは -Mes
オプションで指定する。
package es;
$ENV{LANG} = 'es_ES';
1;
% date -Mes
domingo, 22 de octubre de 2017, 18:00:00 JST
サンプル
サンプルのモジュールがいくつか同梱してある。
-Mutil::filter
util::filter モジュールは、入出力フィルタを集めたモジュールだ。
×tamp
macOS の ping コマンドには --apple-time
というオプションがあるのをご存知だろうか。これを指定すると、RTT だけではなく、パケットが到着した時刻を表示してくれる。
util::filter モジュールの timestamp
関数を使うと、コマンド出力の各行の先頭にタイムスタンプを挿入する。
ping コマンドに使うとこんな感じだ。
~/.optex.d/ping.rc
には、こんな風に書いてある。
option --time -Mutil::filter --of ×tamp
util::filter
モジュールをロードして、出力フィルタとして timestamp 関数を指定している。
試しに、両方指定してみるとこんな風になった。誤差は1ミリ秒以下だ。
timestamp
関数の引数で format
を指定することもできる。デフォルトは %T.%f
で %f
はマイクロ秒、%L
はミリ秒を表す。%dN
の形式で精度を指定することができて %f
と %L
は、それぞれ %6N
、%3N
と同じだ。他にも strftime
がサポートする形式を指定することができる。
App::sdif
に含まれる watchdiff
コマンドと組み合わせてこんな風に使うこともできる。
optex -Mutil::filter --osub timestamp=format='%F %T.%L' watchdiff -pr0 uptime
--if
--if
オプションにはコマンドを指定することができるので、こんな設定を ~/.optex.d/default.rc
に置いておくと、すべてのコマンドの標準入力で圧縮データを入られるようになる。
option --gz -Mutil::filter --if 'gunzip -c'
これだと圧縮ファイルは指定できないが、次の process substitution などと組み合わせれば実現可能だ。
--io-color
標準出力とエラー出力が混ざって見にくいと感じたことはないだろうか。util::filter
モジュールで定義されている --io-color
オプションを使うと、標準エラー出力の色を変えて出力してくれる。自分の ~/.optex.d/default.rc
には次のように指定してあるので optex を通じて実行すると、すべてのコマンドのエラー出力が着色されるようになっている。
autoload -Mutil::filter --io-color --set-io-color
option default --io-color
デフォルトでは 555/201;E
という色を指定してあるので、気に入らなければこれを変えてもいいし --set-io-color
オプションを使って好きな色を設定してもいい。色の指定方法については perldoc Getopt::EX::Colormap
を参照して欲しい。ここで使っているのは RGB 6x6x6 216色の色指定で、555 は白で 000 が黒だ。E
は実はマイブームの機能で、行削除のシークエンスを出力する。この時、背景色が指定されていると、その色で行末までを塗りつぶすので、上の画像のような結果になる。
option --set-io-color &io_color($<shift>)
option --io-color --set-io-color STDERR=555/201;E
-Mutil::argv
util::argv モジュールは、コマンド引数を何とかするモジュール。
たとえば、times 関数を使うと、引数を増やすことができる。
$ optex echo -Mutil::argv::times=count=3 a b c
a a a b b b c c c
まあ、こんなのはあまり役には立たない。
自分はずっと tcsh を使っていて、process substitution の機能を使いたい時だけ bash を使っていたのだが、最近ついに30年以上使い続けた tcsh からログインシェルを bash に移行した。
これも、util::argv::proc 関数を使うと実現できる。
インストール
とりあえず、
$ cpanm App::optex
でインストールできるはず。
cpanm がない場合は、cpanminus で検索してインストールしてください。
追記: コンフィグレーションファイル
~/.optex.d/config.toml
で設定情報を指定できるようにした。
alias
今までのままでも、別の名前でリンクを作って設定ファイルを作れば alias 的な使い方はできたのだが、設定ファイルの中で指定できるようにした。次のように、文字列か配列で指定できる。
[alias]
pgrep = [ "greple", "-Mperl", "--code" ]
hello = "echo -n 'hello world!'"
no-module
元々 Getopt::EX
を使っているコマンドは -M
オプションを解釈するので、optex
から呼ぶと混乱する。そのため no-module
と指定してあるコマンドについてはモジュールオプションを解釈しないようにした。
no-module = [
"greple",
"pgrep",
]