シェルスクリプトで引数オプションを使いたい
my_shell_script -a /usr/bin
getopts を使えば引数が取れる
bash では getopts を使えば、引数の処理を簡単に行うことができて便利。ruby などでは optparseに相当するやつです。
はじめに getopt / getopts の違いについて
全ての初めに。
getoptsについて読む前に知っておかなくちゃいけないことがある。
getopts コマンドとシェルのビルトインの二種類がある。
名前 | 種類 |
---|---|
getopts | シェル組み込み |
getopt | コマンド・ファイル |
Google検索はgetopt/getopts を区別しないで結果を出すので注意が必要でした。
今回、調べたのは getopts です。
getopt は/usr/bin にあるコマンドです。
/usr/bin/getopt は、BSD(OSX)とGNU(linux )で挙動が異なるため、OSXとLinuxでどちらでも使えるようにするため、gnu bash の組み込み関数を使うことにしました。
getopts はシェル組み込み関数です。
今回扱うのは、getoptsシェル組み込み関数です。
getopts の基本的な使い方
getopts の基本的な使い方 は次の通りです。
getopts "引数名" 変数名
使用例
使い方は簡単です。
getopts "a" opts
引数に -a を指定したら opts 変数に中身(a)が入る
複数オプションを入れる
複数書きたいときは、連続してアルファベットを続ける
getopts "abc" opts
オプションで使いたいアルファベットを複数続けて書くだけ。楽チン。
実際に使って試してみる。
適当に関数を作ってgetopts を使ってみます。
function my_func(){ echo 引数=$1 getopts "adh" opts echo getopts結果=$opts } my_func -a
実行結果
引数=-a getopts結果=a
未知の引数の場合
my_func -b
実行結果
引数=-a /Users/takuya/Desktop/test.sh: illegal option -- b getopts結果=?
何も書かなくても未指定の引数にはエラーを出してくれる。便利!
シェルの言語がja_JPであれば
takuya@~$ my_func -x -bash: 不正なオプションです -- x
エラーメッセージをローカライズして表示してくれる。いいね
エラー処理を自分でやる?
組み込みのエラー処理はいらないと思う場合、
illegal option –を無視するには
先頭にセミコロンを置く
getopts ":adh" opts
こうすると、エラーを自分でハンドリングできる。
複数オプション の例
先ほどの関数に複数オプションをつけてみると。。。
my_func -a -d 引数=-a -d getopts結果=a
複数オプションは考慮されない。 (後述するが、複数回getoptsを呼び出すことで処理できる。)
オプションを指定しない場合
getopts を設定した関数へ、なにもオプションを指定せずに起動すると・・・
my_func 引数= getopts結果=?
結果は ? になる。
そのため、"?“ を判定してヘルプを表示するようスクリプトを記述する。
”?”(未知=ヘルプ)などと覚えておけばいいでしょう。
-a オプションではなく、ただの引数を入れる
ハイフン付きのオプションでななく、よくある引数を入れてみるとどうなるか。
my_func aaaaaaaaa 引数=aaaaaaaaa getopts結果=?
引数がない時と同じになる。
オプション引数を扱う。
オプション付き引数を入れる。つまり、オプションで引数を入れてみる。
my_func -a my_value
このように、設定値付きのオプションを使う、オプション引数を使う。
“a:” 文字+”:”でオプション引数
オプションに引数をつけるには、getopts で「:」を追加する
引数が一つのとき
getopts "a:" OPT
引数が二つのとき
getopts "a:b:c:" OPT
引数が三つのとき
getopts "a:b:c:" OPT
オプション付き引数を処理しない場合
オプション付き引数を処理しない場合は連続して文字を続けるだけ
getopts "abcdefg" OPT
オプション引数のサンプル
シェルスクリプトは次のように記述した $OPTARG にオプション引数が入る点が違う。
function my_func(){ echo 引数=$@ getopts ":a:" opts echo getopts結果=$opts echo $OPTARG } my_func -a aaaaaaaaa
実行結果は次の通り
引数=-a aaaaaaaaa getopts結果=a aaaaaaaaa
ポイントは、$OPTARG にオプションの引数が入る
複数オプションを使う
ここまでで、基本的な使い方がわかったので、実際にありそうなオプションを取り扱ってみたいと思う。
そのままでは複数オプションを扱えないので、複数回 getopts を使う。
複数回の getopts 呼び出し
先ほど、複数個のオプションを処理できないと書きましたが、getoptsを複数回呼び出せば、複数個のオプションを処理できます。
function my_func(){ echo args=$@ getopts ":a:b:" OPT echo $OPT echo $OPTARG getopts ":a:b:" OPT echo $OPT echo $OPTARG } my_func -a aaaaaaaaa -b bbbbbbbbbbbbb
このようにgetoptsを何回も処理したら
実行結果が次のようになる。
args=-a aaaaaaaaa -b bbbbbbbbbbbbb a aaaaaaaaa b bbbbbbbbbbbbb
ここでのポイントは。複数回getoptsを適用すれば、複数個のオプションに対応できる。という点
複数オプションをループで処理する
ここまできて、漸くネットで見かけるサンプルに近くなる。
function my_func(){ echo args=$@ while getopts ":a:b:" OPT ; do echo $OPT echo $OPTARG done } my_func -a aaaaaaaaa -b bbbbbbbbbbbbb
複数オプションを取得するには、なくなるまでwhileループを回すのが基本
実行結果
args=-a aaaaaaaaa -b bbbbbbbbbbbbb a aaaaaaaaa b bbbbbbbbbbbbb
while で処理するときは caseとともに使う。
function my_func(){ echo args=$@ while getopts ":a:b:" OPT ; do case $OPT a) echo $OPT echo $OPTARG ;; b) echo $OPT echo $OPTARG ;; esac done } my_func -a aaaaaaaaa -b bbbbbbbbbbbbb
これで、getopts を使える。
getopts と while + case を組み合わせて戦うことで、シェルスクリプトでもオプションと引数つきオプションを扱えることがわかった。
ただし、whileループで処理した場合引数なしには対応できない
while ループを記述したコマンドを オプションなしで起動すると・・・
my_func aaa args=aaa (getopts のwhile ループでは処理されない
引数が指定されてない時に注意が必要
ナニも引数が指定されない場合は、getoptsはシゴトをするが、exit(1) するのでwhile と相性が悪い
while に入れた時には、結果は空っぽになる。
“?” はどこいった?
ただし、"?“ はきちんと処理されている。 while が処理終了になるので、while 中の case に処理がこないだけ。
function my_func(){ echo args=$@ getopts ":a:b:" OPT echo $? echo opt=$OPT } my_func -a hoge my_func
結果
args=-a hoge 0 opt=a args= 1 opt=?
OPTには ? が入るが、コマンドの実行結果ステータスが 1 になるので whileから抜けてしまう。
なので、while getopt でオプションが全く指定されない場合の ?をwhile中で検出することが出来ない。
ここまでのまとめ
ここまでの実験でわかったことをまとめた。
- getoptsはオプション・引数付きオプションを処理することができる。
- getoptsは複数回起動すると複数のオプションを処理できる
- getoptsは while と case 句を組み合わせて処理をする。
function my_func(){ echo args=$@ while getopts ":a:b:" OPT ; do case $OPT in a) echo -a の引数 $OPTARG ;; b) echo -b の引数 $OPTARG ;; : ) echo "ここ" ;; \? ) echo nothing matched ;; esac done } my_func -a aaa my_func my_func -c ccc
while とともに使った場合
検出ができるのは、「不正なオプション」を渡した時に限られる、「オプションが全く未指定」は検出ができない。別途考えないとダメなようだ
注意: getoptsをfunction内で利用す場合
getopts 複数回よびだせる、そのときにどこまで処理したかを保存する。
どこまで処理したかは、変数 $OPTIND(オプションインデックス)に保存する。
そのため、getopts は OPTINDを使うので、function で使うときは、OPTIND をグローバル汚染させない注意が必要
function path() { local OPTIND OPTARG OPT ## ここ重要 while getopts 'ha:d:' OPT ; do case $OPT in d) echo "-D entered" ;; a) echo "-A entered" ;; \?) ;; esac done }
local OPTIND を書かないと、グローバル変数に値が入っちゃうので、getopts が含まれる関数を何度も呼び出せない。
local 宣言があるとき
function 内に local 宣言をちゃんと書いた場合・・・
function path() { local OPTIND OPTARG OPT while getopts 'ha:d:' OPT ; do case $OPT in a) echo "-A entered" ;; esac done echo end } ### ここで関数の実行 path -a aaa path -a aaa path -a aaa
実行と結果
-A entered end -A entered end -A entered end
local 宣言がないとき。
function path() { # local 宣言をコメントアウト #local OPTIND OPTARG OPT while getopts 'ha:d:' OPT ; do case $OPT in a) echo "-A entered" ;; esac done echo end } path -a aaa path -a aaa path -a aaa
実行結果
-A entered end end end
このようにlocal宣言がないと変数$OPTIND が作用して、オプションを取れなくなる。
1つのファイルでシェルスクリプトを記述するのなら問題ないのだけれど、関数を作って連続して使うなら変数を共有しない工夫が必要
getoptsをファイルで使う場合
getoptsをファイルで使う場合は、環境変数をexport しない限りファイル内部で完結するので、管理は難しくない。
my_script.sh
#!/usr/bin/env bash while getopts 'ha:d:' OPT ; do case $OPT in a) echo "-A entered" ;; esac done echo end
ファイルに記述した場合
my_script.sh -a aaaa my_script.sh -a aaaa my_script.sh -a aaaa
としてもOPTINDはファイルごとに確保されるため全然影響がなく、問題ない。
getopts まとめ
- getopts はシェル組み込み関数(builtinコマンド)
- getopts “abc” とかけば -a -b -c とオプションを作れる
- 複数個のオプションを一度に処理するには getoptsを複数回呼び出し
- 複数個のオプションを処理するには while + case を使う
- オプション引数を使うには getopts “a:b:c"とする
- エラー処理を自分でやるには getopts “:a” と先頭に「 : 」をつける
- 関数内で getopts を使うときは local OPT をつける
参考資料
http://stackoverflow.com/questions/16654607/using-getopts-inside-a-bash-function