シェルスクリプトでgetopts
を使うと簡単に引数のオプションが解析が出来ますが、
ポジショナルな引数と一緒に使おうと思うと
解析部分が途中で終わってしまったりうまく行かないことがあります。
ただ使う側としては位置を気にせず使えたほうが便利なことが多いので それを実装する方法について。
getoptsを使ったオプション解析
よくある感じはこんなの。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
getopts
の後に-
始まりで使いたいオプションとして使いたいアルファベットを書きますが、
値を持つものに関しては:
を後ろにつけます。
このオプションのアルファベットがOPT
に入り、
値持ちの場合はOPTARG
にこの値が入ります1
それぞれのオプションに関してcase
文でそれぞれに対しての処置を設定します。
もし-
付きの引数でgetopt
に定義されてないものがくるとOPT
には?
が入るのでそれに対する処置も追加しておきます。(エスケープ(\?
)が必要。)
解析が一通り終わった後、解析に使われなかった残りの引数を取得するために
解析に使われた分(OPTIND - 1
)2
だけshift
で削除して残りをポジショナルな引数($1
, $2
, …)として使えるような形にしています。
実行してみると、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
みたいな感じになります。
オプション部分に関しては順番は入れ替わっても大丈夫ですし、
複数回同じオプションを使ってもその度に解析されるので、
上の例だとただ値を入れてるだけなので-c zzz -c ZZZ
みたいにした場合は後の方が残っています。
ただ、これをオプションより前にポジショナルな引数を与えてしまうと
1 2 3 4 5 |
|
といった感じで解析されません。
また、a
は値を持たないオプションですが、-a
の後ろに引数を置くとポジショナルな引数としてそこで解析が終わるので、
1 2 3 4 5 |
|
といった感じに-a
の解析だけ行われて終了してしまいます。
ポジショナルな引数をオプションの前や間にも入れられるようにする
これに関してはstackoverflowに10年以上前の質問がありました。
一番voteされてるのは普通のgetopts
の説明で答えになってない感じではあるんですが、
8年経って投稿された答えがいい感じに解決してくれてます。
上のスクリプトを下のように書き換えてみます。
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 |
|
これを実行すると
1 2 3 4 5 |
|
といった感じでオプション解析に使われてないものが
positional
に入れられてそれを順に使えるような状態が出来ます。
ここでは
1 2 |
|
の組み合わせにすることでgetopts
で1つずつ解析していき、
かつ-
始まりでないポジショナルな引数が来たらこの2段目がfalseになるので、
そこでは
1 2 |
|
としてpositional
変数にその値を追加しています
3。
この場合はgetopts
でのOPTIND
のインクリメントが行われないので
手動で1つ値を加えておきます。
こうすることで最後までオプション引数があれば解析してその他はポジショナルな引数として扱うことが出来ます。
オプションでない-
で始まる引数をポジショナルなものとして扱う
上のスクリプトでオプションとして設定していない文字を-
付きで与えると、
1 2 3 |
|
といった感じのエラーを出します。
このような設定していないものはそのままポジショナルな引数として使いたい、といったことがある場合は以下のようにすることで扱えます。
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 |
|
変わったところはgetopts
の引数の最初に:
が追加され、?
の場合に
positional
に-
を加えたOPTARG
を追加している部分。
getopts
の第一引数の文字列で一番最初に:
を入れると
-
から始まる引数が指定のオプションでない場合はエラーにせずに
その文字をOPTARG
に入れる、という仕組みがあります。
この場合もOPT
に入るのは?
です。
これを使って-$OPTARG
をpositional
に加えることでオプション指定していない-
引数をポジショナルな引数として扱っています。
1 2 3 4 5 |
|
こんな感じ。
引数の一部を解析した上で さらに別の関数にそのまま渡したい、とか言う場合に使えそうです。