最近、以前作った getoptions の改良をしていました。(以前の記事はこちら)
参考 こちらもどうぞ
「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
は実行されないという仕組みです。