シェルスクリプトをもっと手軽に作れるようになるSimpleShellWrapperを作ってみた
趣味的にも仕事的にも、ちょっとしたシェルを書くことが良くあるのですが、やっつけで作ることも多いです。
その結果何が起こるかというと
- 必須のコマンドライン引数を付け忘れて実行して、大惨事
- 引数の順番を間違えて大惨事
- working directoryを指定してなかったので、ゴミが色々できる。あるいは、古い一時ファイルを中途半端に読み込んで大惨事
- 半年前に書いたコードだから使い方を忘れて大惨事
とかがよくあります。もちろん回避方法はあります。ちゃんと引数チェックをするコードを書いたり、"--target"みたいなオプションを実装して可読性を上げたり、"-h"で呼べるヘルプを作ればいいわけです。しかし、こうしたありきたりのコードを毎回書くのは面倒ですよね。
シェル書いている人は、だいたいこういう経験をするのではないでしょうか?
- 生まれたて: 引数チェック? 何それ美味しいの?
- 幼年期の終わり: 俺はもう事故りたくないんだ! と 地道にチェックを入れる
- 思春期: ふつうのシェルには興味がありません! rubyshellやscala.sys.processを使って、俺Tueeする
- 新卒: 動かない...だと... とふつうのシェルしか無い環境に絶望する
- 社会人: 運用でカバー
最後に始まりに戻るわけです。無限ループって怖いですね。
まあ、本当にヤバイコードには最低限のチェックを入れるようになるので、労力の最適化という感じでしょうか。
この辺他の言語ならどうするかというと、メタなライブラリ使ったり、コンテナ型のアーキテクチャを使います。
というわけで車輪の再発明とか細かいことは気にせずに、それっぽいラッパーを書いて見ることにしました。
実装前に考えた要件としては
- コマンドライン引数が足らなければ叱ってくれる
- デフォルト値はあるけど、オプションで簡単にオーバーライドできる
- 可能な限り、既存シェルには手を加えない
- サーバ管理者と調整しなくても簡単に導入できる
となります。4が結構大事です。どこでも動かないと意味が無いので。というわけで今回はrubyじゃなくてあえてPythonで書きました。これなら最近のLinux環境ならまずどこでも動くと思うので。
実装したものはこちら。
SSWと言っても新鮮な魚でレスリングではなく、SImple Shell Wrapperの略です。
基本的にはシェルスクリプトの先頭で一度sswに自分自身を読み込ませて、sswで解析 & 変換を行い実行する方式です。
#!/bin/sh echo "$0" echo "$@" exec python bin/ssw.py "$@" < "$0" !#
シェル自体は#@というアノテーションを使って説明部分、宣言部分、実行部分に分けています。
説明部分
#@ descript: # $Id$ # This script is generate mkdir, chmod, chown. # output 4 files on working direcotry. # # @usage: %prog [options] MSG1 MSG2 #
基本的にこの部分は無視されるのですが@usageはhelpオプションのusageになります。
この辺はjavadoc的な思想でコード上のコメントとしても可読性が高くなり、かつその情報を元に機械的にドキュメント(この場合はhelpオプション)を生成します。
宣言部分
#@ define: MSG=${1} #@ -m, --message, this is simple echo messsage. MSG2=${2}/hogehoge #@ -M, --message2, this is simple echo messsage2. INPUT_DIR=. #@ -t, --target, scan target direcotry. DIR_LIST_FILE=dir.list
変数の横に#@以下を書くとオプションを実装できます。
オプションが無いときは現在代入されてる値がそのまま使われますが、指定されると、オプションが優先して上書きされます。
また、${1}や${2}で通常のシェル同様コマンドライン引数を使えます。コマンドライン引数は必須なので、宣言時に指定したのに実行時に付け忘れてると下記のようなエラーになります。
ValueError: This line paramater is not found.(MSG=${1} #@ -m, --message, this is simple echo messsage.)
これで引数忘れも怖くない! 1点制約としては$1みたいに{}を省略すると解析対象から外れるのでそこだけ注意です。
実行部分
#@ main: echo "hello $HOSTNAME `hostname` $MSG" echo $MSG2
これはもう普通にシェルそのものです。今のところアノテーションも無し。1点注意としてはコマンドライン引数の変数をそのまま使えないことでしょうか。宣言部で必ず変数に代入してください。
実行は下記のように普通にシェルとして実行します。
sh ./gen_dirbuilder.ssw.sh
まとめ
さて、これで手軽に基本的な機能がそろったシェルを作れるようになりました。
ちなみに全体としてはこんな感じになります。
https://github.com/koduki/ssw/blob/master/gen_dirbuilder.ssw.sh
ssw.py自体はさすがにサーバ上にインストールしないといけませんが、ホームディレクトリ以下とかスクリプト置き場に一緒にという感じで対応すれば、まあ良いでしょう。
これで、ありがちなミスが減って、一人でも悲しい気持ちになる人が減ればいいと思います。
それでは、Happy Hacking!