getoptsコマンドはbashでの組み込みコマンドのひとつで、シェルスクリプトでオプションを実装する際に役立つコマンドになります。getoptsコマンドは使用するオプションのためのオプション文字列とオプションを解析した結果を入れる変数を引数にして、ループを利用してオプションを解析します。
ただし、getoptsコマンドで利用できるオプションの種類には制限があります。また、オプションでの引数も一つに制限されています。しかし、簡易的なオプションのあるシェルスクリプトを作成するのに便利であり、より洗練されたスクリプトを作成するのに役立ちます。
getoptsコマンドで解析できるオプションについて
getoptsコマンドは一文字の-a, -b, -cのような、'-'と一文字のアルファベットを組み合わせたオプションを解析できます。
1 |
./script.sh -a -b 'arg1' 'arg2' |
また、オプションには一つの引数を指定できます。
1 |
./script.sh -a 'aopt_arg' -b 'bopt_arg' 'arg1' 'arg2' |
'-'と複数のオプションを組み合わせた以下のような形式もオプションとして指定できます。
1 |
./script.sh -ab 'bopt_arg' 'arg1' 'arg2' |
ロングオプションや複雑なオプションを利用したい場合はgetoptコマンドや自前のオプション解析器を利用する必要があります。
引数なしのオプションの利用
getoptsコマンドは2つの引数を準備して利用します。
1つ目はオプションに使用する1文字オプションを列挙したオプション文字列になります。
2つ目はオプションの解析結果を入れる変数になります。
2引数のgetoptsコマンドは位置パラメータを解析して、2つ目の引数の変数に結果を入れます。
また、無効なオプションが見つかった場合は'?'の文字が変数に入ります。'?'は任意の一文字に該当するのでエスケープ処理を行っています(ただし、case文の最後の処理になっているので必要性はないかもしれません)。
getopts_demo1.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#!/bin/bash while getopts "abc" opt; do case "$opt" in a) echo "option a demo" ;; b) echo "option b demo" ;; c) echo "option c demo" ;; \?) exit 1 ;; esac done shift $((OPTIND - 1)) while [ $# -gt 0 ]; do echo "PROGRAM_ARG: $1" shift done |
実行例
1 2 3 4 5 6 7 8 9 10 11 |
$ ./getopts_demo1.sh -a -b -c aaa bbb ccc option a demo option b demo option c demo PROGRAM_ARG: aaa PROGRAM_ARG: bbb PROGRAM_ARG: ccc $ ./getopts_demo1.sh -a -b -d aaa bbb ccc option a demo option b demo ./getopts_demo1.sh: 不正なオプションです -- d |
3引数以上でgetoptsコマンドを利用する場合は、位置パラメータの代わりに3つ目以降の引数が利用されます。
1 2 3 4 5 6 7 8 |
$ getopts "abc" opt -a -b -d $ echo $opt a $ getopts "abc" opt -a -b -d $ echo $opt b $ getopts "abc" opt -a -b -d bash: 不正なオプションです -- d |
オプションのあるスクリプトについて
上のgetopts_demo1.shではオプションが入ってきたら、echo文を実行するといったプログラムの処理を行うような実装になっています。このとき、同じオプションが複数回実行された場合、
1 2 3 |
$ ./getopts_demo1.sh -a -a option a demo option a demo |
のように実行されます。これが意図したものなのかは分かりませんが、オプションの処理としては通常、望ましいものではありません。
なので、オプションが入ってきたの時の処理は、可能な限り変数にフラグを立てるやデータを入れる等のような文だけにして置き、オプションの解析が終わった後で、プログラムの処理を行ったほうがプログラムの制御が行いやすい構造になります。
また、このようにすることでデフォルト値を設定でき、どのオプションを常に利用するのか又はしないのかの選択もできるようになります。
getopts_demo2.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
#!/bin/bash aoptfl=false boptfl=false coptfl=false while getopts "abc" opt; do case "$opt" in a) aoptfl=true ;; b) boptfl=true ;; c) coptfl=true ;; \?) exit 1 ;; esac done if $aoptfl; then echo "option a demo" fi if $boptfl; then echo "option b demo" fi if $coptfl; then echo "option c demo" fi shift $((OPTIND - 1)) while [ $# -gt 0 ]; do echo "PROGRAM_ARG: $1" shift done |
実行例
1 2 |
$ ./getopts_demo2.sh -a -a option a demo |
また、関数に慣れているならgetopts_demo2.shのスクリプトは
getopts_demo3.sh(一部省略)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#!/bin/bash function main() { local aoptfl=false local boptfl=false local coptfl=false opt_parse "$@" opt_process prog_process "$@" } function opt_parse() {} function opt_process() {} function prog_process() {} main "$@" |
のようなブロックにしても良いかもしれません(関数の中身は一部のみ記述し、他は省略しています)。
変数のスコープは、関数が終了するまで生きています。また、変数を宣言した関数が呼び出した関数内でもその変数は参照できます。ただし、そのような変数の参照は保守性の観点からむやみに使う方法ではなく、使うべき場所を制限して利用すべきかもしれません。
サイレントモード
使用するオプションの文字列の最初の文字を":"にするとgetoptsコマンドはサイレントモードになります。独自のエラーメッセージを設定したい場合に利用できます。
このサイレントモードのとき、無効のオプションが見つかった時にそのオプションは変数OPTARGに入ります。また、引数が必要なオプションの場合は変数に':'が入り、変数OPTARGに該当しているオプションの文字が入ります。
getopts_demo4.sh(opt_parse関数のみ)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
function opt_parse() { local options=":a:bc" while getopts "$options" opt; do case "$opt" in a) aoptfl=true ;; b) boptfl=true ;; c) coptfl=true ;; \?) echo "Invalid Option: -$OPTARG" exit 1 ;; :) echo "Required Argument: -$OPTARG" exit 1 ;; esac done } |
実行例
1 2 3 4 |
$ ./getopts_demo4.sh -d aaa Invalid Option: -d $ ./getopts_demo4.sh -a Required Argument: -a |
':'と'\?'の位置を入れ替えると'\?'のエスケープ処理の必要性はなくなるかもしれません。
引数があるオプションの利用
引数があるオプションを利用したい場合は、使用するオプションの後に':'の文字を入れます。
また、オプションの引数は変数OPTARGに入ります。
getopts_demo5.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
#!/bin/bash function main() { local aoptfl=false local boptfl=false local coptfl=false local aoptdata='' local coptdata='' opt_parse "$@" opt_process prog_process "$@" } function opt_parse() { local options="a:bc:" while getopts "$options" opt; do case "$opt" in a) aoptfl=true aoptdata="$OPTARG" ;; b) boptfl=true ;; c) coptfl=true coptdata="$OPTARG" ;; \?) exit 1 ;; esac done } function opt_process() { if $aoptfl; then echo "option a demo" echo "option a data: $aoptdata" fi if $boptfl; then echo "option b demo" fi if $coptfl; then echo "option c demo" echo "option c data: $coptdata" fi } function prog_process() { shift $((OPTIND - 1)) while [ $# -gt 0 ]; do echo "PROGRAM_ARG: $1" shift done } main "$@" |
実行例
1 2 3 4 5 6 7 8 |
$ ./getopts_demo5.sh -a data1 -b -c data2 aaa bbb option a demo option a data: data1 option b demo option c demo option c data: data2 PROGRAM_ARG: aaa PROGRAM_ARG: bbb |
複数の引数のオプションとして利用する場合は、例えば、カンマ区切りの文字列を要求して、その文字列を区切り文字で分割することで複数の引数を要求するオプションのように利用できます。
文字列を配列にする方法はreadコマンドを利用する方法や配列のリテラルを利用する方法があります。以下は配列のリテラルを利用した方法になります。
getopts_demo6.sh(opt_parse関数とopt_process関数、中身は一部省略)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
function opt_parse() { local options="a:bc:" local IFS=',' while getopts "$options" opt; do case "$opt" in a) aoptfl=true aoptdata=($OPTARG) ;; # 省略 esac done } function opt_process() { if $aoptfl; then echo "option a demo" for data in "${aoptdata[@]}"; do echo "option a data: $data" done fi # 省略 } |
実行例
1 2 3 4 5 6 7 8 9 10 |
$ ./getopts_demo6.sh -a data1,data2,data3 -b -c data4 aaa bbb option a demo option a data: data1 option a data: data2 option a data: data3 option b demo option c demo option c data: data4 PROGRAM_ARG: aaa PROGRAM_ARG: bbb |