LinuxやUNIXを使っていると、自前のBashスクリプトを書くことが多いと思いますが、そのスクリプトが何の引数を受け取るのか覚えていないことがよくあります。 こんなときに、わざわざスクリプトの中身を見て判断するのはめんどくさいので、使い方を間違っているときにUsageを出力するようにするといいのですが、スクリプトの変更を繰り返した結果、このUsageが正しい引数を提示しているかどうかも怪しいことがあります。
例えば、引数を2つ取るspamham.sh
という以下のようなスクリプトを作るとします。
#!/usr/bin/env bash readonly program=$(basename $0) function print_usage_and_exit() { echo >&2 "Usage: ${program} SPAM HAM" exit 1 } if [ $# -ne 2 ]; then print_usage_and_exit fi readonly spam=$1 readonly ham=$2 echo "SPAM: ${spam}" echo "HAM: ${ham}"
このスクリプトは引数を2つしか取らず、それ以外の数の引数を渡すとUsageを吐いて終了します。
$ ./spamham.sh Usage: spamham.sh SPAM HAM $ ./spamham.sh hoge Usage: spamham.sh SPAM HAM $ ./spamham.sh hoge piyo SPAM: hoge HAM: piyo $ ./spamham.sh 1 2 FIZZ Usage: spamham.sh SPAM HAM
このスクリプトのままでは引数についての記述が少し冗長で、引数の数なり順番なりを変更するのがめんどくさくなります。
ですので、(spam ham)
のように引数を受け取る変数名を列挙するだけで引数を受け取れるようにしましょう。
過程を省きますが、(僕の中では)最終的にこのような形に落ち着きました。
#!/usr/bin/env bash readonly program=$(basename $0) readonly args=(spam ham) function print_usage_and_exit() { echo >&2 "Usage: ${program} $(IFS=' '; echo ${args[*]^^})" exit 1 } if [ $# -ne ${#args[@]} ]; then print_usage_and_exit fi for arg in ${args[@]}; do eval "readonly ${arg}=$1" shift done echo "SPAM: ${spam}" echo "HAM: ${ham}"
引数をargs=(spam ham)
として定義して、print_usage_and_exit
の中では
IFS=' '; echo ${args[*]^^}
として引数の名前を大文字表記で列挙しています。
また、引数の数は配列args
の要素数なので${#args[@]}
で取得できます。
最後に、各引数変数に引数の値を代入するところは、eval
とshift
を使って、次々に変数に$1
を代入してはshift
してを繰り返して実現しています。
こうすることで、引数を変更するときは、args=()
の中身を書き換えるだけで済むようになりました。