最近、以前作った getoptions の改良をしていました。(以前の記事はこちら)
以前のバージョンはシェルスクリプトライブラリとして使い方を想定していたのですが getopt や getopts の代わりとして外部コマンドとしても使えるようにしました。使い方は簡単で ここ から getoptions をダウンロードしパスが通ってる場所(~/bin や /usr/local/bin/ など)にインストールするだけです。それだけでもう次のシェルスクリプトが動作します。他に設定ファイルや事前のビルド作業なども不要なので学習曲線は低くメンテナンス性も抜群です。
#!/bin/sh
VERSION="0.1"
parser_definition() {
setup REST help:usage -- "Usage: example.sh [options]... [arguments]..." ''
msg -- 'Options:'
flag FLAG -f --flag -- "takes no arguments"
param PARAM -p --param -- "takes one argument"
option OPTION -o --option on:"default" -- "takes one optional argument"
disp :usage -h --help
disp VERSION --version
}
eval "$(getoptions parser_definition -) exit 1"
echo "FLAG: $FLAG, PARAM: $PARAM, OPTION: $OPTION"
printf '%s\n' "$@" # rest arguments
見ての通り必要なのはサポートするオプションを書いた定義関数のみです。getoptions を外部コマンドとして使えるようにしため、シェルスクリプトライブラリの読み込みが不要になりました。さらにオプションを解析する際の定型的なコードも減りたった一行になっています。もうこれ以上減らすことはできないでしょう。
今まで通りのライブラリとしての使い方もさらに便利になっています。ライブラリとして使うと getoptions をインストールせずに使えますしオプションパーサーを生成してシェルスクリプトに埋め込むとライブラリすら不要になります。その埋め込み作業もツール(gengetoptions)を用意しているので簡単です。
bash だけでなく全ての POSIX シェルに対応していますし、ロングオプションやロングオプションの省略、サブコマンド、ヘルプの自動生成、バリデーション、エラーメッセージのカスタマイズや、オプション指定時に関数を呼び出すなど高度な機能にも対応しています。これからは getoptions をインストールするだけで(例えば自分用の)シェルスクリプトをサクッと作れるようになったというわけです。
ちょっとだけ技術解説
さてシェルスクリプトに詳しい人であれば、上記のコードを見て珍しいテクニックが使われていることに気づくかもしれません。一つは外部コマンドの getoptions からシェル関数 parser_definition を呼び出しているということです。シェル関数の getoptions あれば同じプロセスのシェル関数を呼び出すのは容易な話ですが、外部コマンドの getoptions は別のプロセスであるため呼び出し元シェルスクリプトのシェル関数を呼べるはずがありませんね? eval を使っているところからわかるかもしれませんが、実は外部コマンドの getoptions はシェルスクリプトコードを出力します。出力されたシェルスクリプトコードには getoptions シェル関数とそれを呼び出すコードが含まれているため、外部コマンドからシェル関数を実行したかのように動作するのです。
もう一つは見慣れない書き方である eval "$(...) exit 1" です。最後の exit 1 はなんなのでしょうか? 実はこれは getoptions がインストールされてない場合に、エラー終了させるためのコードです。シェル関数ライブラリとしての使い方であれば getoptions シェル関数が存在すると想定できますが、外部コマンドの場合はインストールしてないかもしれません。その場合は $(...) の部分で getoptions: command not found を標準エラー出力に出力しつつ空文字となるため exit 1 が実行されます。もし getoptions がインストールされている場合、生成されたシェルスクリプトの最後で # Do not execute というコメントを出力します。つなげると # Do not execute exit 1 となり exit 1 は実行されないという仕組みです。
↧