シェルスクリプト オプション解析 徹底解説 (getopt / getopts)

## はじめに

シェルスクリプトでオプション・引数解析といったらまず挙がるのが `getopt` と `getopts` です。さてどちらを使うべきでしょうか？始めに断っておくと実は私はどちらも積極的には使っていません。なぜなら独自実装でもほとんどコードは変わらず、より柔軟な処理ができるからです。とはいえ `getopt` と `getopts` はシェルスクリプトの基本なのでこれらの使い方について解説したいと思います。（解説が不要な人はそれぞれの「使用方法」を読んでください。）

## 本編の前に

この記事を書いたあと独自実装のオプション解析コードを書き最終的に `getopt` や `getopts` よりも高機能で使いやすいオプションパーサー [getoptions](https://github.com/ko1nksm/getoptions) を開発しました。もはやちまちまとしたコードを手書きする作業は不要です。**シェルスクリプトで簡単に引数解析したいだけという方にはこちらをおすすめします**。POSIXシェル準拠で `getoptions` コマンドをインストールするだけで環境依存せずに使えますし、オプションパーサーを事前生成すれば `getoptions` なしで動かすこともできます。詳細は以下のリンクから（時系列に関連記事を並べてます。基本的に最後の記事を見ればよいです。）

- [高機能で短いシェルスクリプト用のオプション解析コード（POSIXシェル準拠・独自実装）] (https://qiita.com/ko1nksm/items/7d37852b9fc581b1266e)
- [簡単に使えるエレガントなオプション解析ライブラリ（シェルスクリプト用）](https://qiita.com/ko1nksm/items/1bcc49bef56a5c245251)
- [シェルスクリプト(bash等)の引数解析が究極的に簡単になりました](https://qiita.com/ko1nksm/items/9ee16927b7f8899c4a9e) 

## `getopt` と `getopts` の違い

`getopt` と `getopts` の違いですが、まず `getopt` は外部コマンドで POSIX では規定されていません（関数としては規定されています ([getopt](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getopt.html))。それに対して `getopts` はシェルビルトインコマンドで POSIX で規定されています ([getopts](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/getopts.html))。そのためすべてのPOSIX準拠シェルで実装されている `getopts` の方が移植性が高くなります。

また `getopt` は大きく2種類存在しており、オリジナル版（BSD, macOS 版も同等）は最も機能が少なく GNU 版は高機能です。（このドキュメントでは macOS 版と GNU 版で検証しています。）

英語版 Wikipedia の [getopts](https://en.wikipedia.org/wiki/Getopts) にはこのように書かれています。

> getopts was first introduced in 1986 in the Bourne shell shipped with Unix SVR3.

> getopts was developed as an improvement to the original getopt Unix program.
> The original getopt program had fewer features than getopts.

> An alternative to getopts is the GNU enhanced version of getopt.

つまり歴史的にみると以下のようになると思われます。**（想像です)**

1. より少ない機能を持った `getopt` (オリジナル版) が作られた。
2. `getopt` の機能を強化した `getopts` が作られた。
3. GNU 版では `getopt` を拡張することで `getopts` よりも高機能になった。<br>
  （一方で macOS を 含む BSD 版は、オリジナル版と同等なので機能が少ない。）

それぞれの機能の違いの概略を以下に示します。

|                                  | getopt | getopts        | getopt (GNU版) | 補足                      |
| -------------------------------- | ------ | -------------- | -------------- | ------------------------- |
| ショートオプション               | Yes    | Yes (POSIX)    | Yes            | 例 `-x`                   |
| 結合されたショートオプション     | Yes    | Yes (POSIX)    | Yes            | 例 `-xy`                  |
| オプション引数                   | Yes    | Yes (POSIX)    | Yes            | 例 `-x value`, `-xvalue`  |
| 省略可能なオプション引数         | -      | -              | Yes            | 例 `-x` or `-x value`     |
| 引数に空白を含めることができる   | -      | Yes (POSIX)    | Yes            | `"next day"` のような引数 |
| エラーメッセージの抑制           | Yes    | Yes (POSIX)    | Yes            |                           |
| エラーメッセージのカスタマイズ   | -      | Yes (POSIX)    | -              |                           |
| ロングオプション                 | -      | -              | Yes            |                           |
| オプションとオプション以外の混在 | -      | -              | Yes            |                           |
| - で始まるロングオプション       | -      | -              | Yes            | 例 `-exec`                |
| + で始まるオプション             | -      | zsh, ksh, mksh | -              | 例 `+x`                   |

GNU 版 `getopt` が最も高機能ですが、BSD や macOS を考慮する場合は、`getopts` を使うしかありません。ただしロングオプションは使えません。

  * `homebrew` でよければ GNU版 `getopt` である `gnu-getopt` がインストール可能です。
  * 頑張れば `getopts` を使いつつロングオプションに対応することも出来るようですが、かなり無理した実装ばかりなので、それをやるぐらいなら独自実装の方が簡単です。

さてこれよりそれぞれのコマンドの詳細な使い方について解説したいと思いますが、歴史的な流れに従って「`getopt`（オリジナル版）」「`getopts`」「`getopt`（GNU版）」の順で解説します。

## getopt (オリジナル版)

まず GNU 拡張を含まない（おそらく）オリジナル版の `getopt` の解説です。macOS を含む BSD 版の動作も同じです。オリジナル版の `getopt` は引数に空白文字を含めることが出来ないという大きな欠点があるので推奨しません。移植性があり引数に空白文字を含めることができる `getopts` の方が優れているので、オリジナル版を使う理由はないのですが `getopts` と GNU 版 `getopt` の前提知識として必要なため解説します。

`getopt` は一般的にオプション解析をするためのコマンドと言われていますが、オプション解析をしやすくするために引数を整形するためのコマンドと捉えたほうが正確です。例えば `-ab` のようなオプションを `-a -b` に分離したり、`-xvalue` のようなオプションを `-x value` に分離したりします。

`getopt` の使用方法は以下のとおりです。

```
getopt optstring parameters
```

第一引数の `optstring` にオプションとして解析したい文字（このドキュメントでは「オプション文字列」と呼ぶことにします。）を指定します。`parameters` が `getopt` で整形させる引数です。

`getopt` による出力（整形）の例を以下に示します。

| 呼び出し                          | 出力                       |
| --------------------------------- | -------------------------- |
| `getopt abc -a -b -c`             | ` -a -b -c --`             |
| `getopt abc -abc`                 | ` -a -b -c --`             |
| `getopt abc -a -b -c foo bar baz` | ` -a -b -c -- foo bar baz` |
| `getopt a:bc -afoo -bc`           | ` -a foo -b -c --`         |
| `getopt abc -a foo -b bar -c`     | ` -a -- foo -b bar -c`     |
| `getopt a:bc -a foo -b bar -c`    | ` -a foo -b -- bar -c`     |

※ GNU 版では環境変数 `POSIXLY_CORRECT` を定義すると同等の出力になります。

### getopt の機能

#### ショートオプション

オプション文字列に `a` と指定すると オプション `-a` が使えるようになります。オプションとオプション以外は `--` を挟んで前後に分割されます。

| 呼び出し          | 出力         |
| ----------------- | ------------ |
| `getopt a -a`     | ` -a --`     |
| `getopt a`        | ` --`        |
| `getopt a foo`    | ` -- foo`    |
| `getopt a -a foo` | ` -a -- foo` |

#### 結合されたショートオプション

（オプション引数を取らない）オプションは `-abc` のようにまとめて書くことが出来ます。まとめて書いても `getopt` が分解してくれるのでオプション解析が楽になります。

| 呼び出し          | 出力           |
| ----------------- | -------------- |
| `getopt abc -abc` | ` -a -b -c --` |

#### オプション引数

オプション文字列の各文字の後に `:` をつけると「オプション引数」を持つオプションという意味になります。オプション引数はオプションにつなげて指定する方法と、スペースで区切って次の引数で指定する方法があります。

| 呼び出し          | 出力        | 補足                                                                     |
| ----------------- | ----------- | ------------------------------------------------------------------------ |
| `getopt a: -a bc` | ` -a bc --` |                                                                          |
| `getopt a: -abc`  | ` -a bc --` |                                                                          |
| `getopt a: -a -x` | ` -a -x --` | この場合の `-x` は `-a` のオプション引数であってオプションではありません |

#### エラーメッセージ

不明なオプション、もしくはオプション引数がない場合は、エラーメッセージを出力し終了ステータスが非0で終了します。

| 呼び出し       | エラー出力（例）                              |
| -------------- | --------------------------------------------- |
| `getopt a: -x` | `getopt: 無効なオプション -- 'x'`             |
| `getopt a: -a` | `getopt: オプションには引数が必要です -- 'a'` |

オプション文字列の先頭に `:` をつけるとエラーメッセージの出力を抑制することが出来ます。抑制するのはエラーメッセージの出力だけで、エラーが発生した時の終了ステータスは非0です。終了ステータスでエラーになったことはわかりますが、エラーとなったオプションや理由はわかりません。そのため `getopt` を使用する場合はデフォルトのエラーメッセージを使用したほうが良いでしょう。

### 使用方法

#### 位置パラメータへの設定

まずオプションを解析を行うために `getopt` で整形した引数（出力）を位置パラメータ（`$1`, `$2` ...）に設定します。

```sh
args=$(getopt abc $*) || exit 1
# args=`getopt abc $*` 同等の意味の古い書き方

set -- $args
# eval "set -- $args" # zsh の shwordsplit に対応するにはこちらを使う
```

時折、以下のように変数に入れずにいきなり `set` している例を見かけますが、これだと `getopt` でエラーが発生しても `set` 呼び出しで正常終了になってしまうため間違った使い方です。（同様に `for ... in` に直接渡すのもよくありません。）

```sh
set -- $(getopt abc $*)
```

ところで macOS (BSD) の `man getopt` には以下のような記述があります。

> you should not use \`getopt	abo: "$@"\` since that would parse
> the arguments differently from what the set command below does.

どうやら、`getopt abc "$@"` としてはいけないようです。おそらく `IFS` の値によって、`set` コマンドと異なる解釈になるのを避けるためだと思うのですが納得できていません。`getopt` コマンドは引数をスペース区切りでしか出力しないので、結局の所 `IFS` はスペース区切り前提にするしか無いと思います。釈然としませんがオリジナル版の `getopt` を使うことはないのでこれ以上追求しないことにします。理由をご存じの方はコメントで教えて下さい。

#### オプション解析

位置パラメータへ設定したら次はループを使ってオプションを解析していきます。ループには `for` と `while` がありますが、どちらを使っても殆ど変わりません。

まずは `for` を使った例です。

```sh
# 位置パラメータへの設定
args=$(getopt a:b $*) || exit 1
set -- $args

# オプション解析のためのループ
for opt in "$@"; do # in "$@" を省略して for opt と書くことも出来ます。
  case $opt in
    -a) A_VALUE=$2; shift 2 ;;
    -b) B_FLAG=1; shift ;;
    --) shift; break ;;
  esac
done
```

次に `while` を使った例です。

```sh
# 位置パラメータへの設定 は 同じなので省略

# オプション解析のためのループ
while [ $# -gt 0 ]; do
  case $1 in
    -a) A_VALUE=$2; shift 2 ;;
    -b) B_FLAG=1; shift ;;
    --) shift; break ;;
  esac
done
```

ループ変数 `opt` を使用するか位置パラメータ `$1` を使用するかの違いだけです。`while` の場合に `shift` が必要なのは当然として、なぜ `for` でも `shift` が必要なのでしょうか？ それはオプション引数を参照するためです。`for` を使って現在のオプションが `opt` 変数に代入されたとしても、オプションの次にあるオプション引数はわかりません。処理済みのオプションを `shift` していくことで、オプション引数は常に位置パラメータ `$2` から取得できるようになります。オプションは `opt` 変数、オプション引数は位置パラメータ `$2` と違う方法で取得することになるので個人的には `while` の方が好みです。もしオプション引数が必要ないなら `shift` も必要なくなるので `for` の方がわずかにシンプルになります。

## getopts

`getopt` が引数を整形するコマンドに対して、`getopts` は呼び出すたびに一つずつオプションを読み取る関数です。オリジナル版の `getopt` とは違い引数に空白文字が含まれていても問題ありません。解析したオプションは指定したシェル変数に代入され、オプション引数が存在する場合は `OPTARG` 変数に代入されます。全てのオプションの解析が終わると、`getopts` は 終了ステータスとして非0を返し、`OPTIND` 変数にはオプション以外の残りの引数（オペランド）を処理できるよう、最初のオペランドのインデックス番号が代入されます。シェルビルトインコマンドとして実装されたことによりシェル変数を使うようになったのが大きな特徴で `getopt` に比べてシンプルな使い方になっています。

`getopts` による出力例を以下に示します。

| 呼び出し                               | OPT (実際は指定した名前)        | OPTARG      | OPTIND |
| -------------------------------------- | ------------------------------- | ----------- | ------ |
| `getopts abc OPT -a -b -c`             | 1回目 `a`, 2回目 `b`, 3回目 `c` |             | 4      |
| `getopts abc OPT -abc`                 | 1回目 `a`, 2回目 `b`, 3回目 `c` |             | 2      |
| `getopts abc OPT -a -b -c foo bar baz` | 1回目 `a`, 2回目 `b`, 3回目 `c` |             | 4      |
| `getopts a:bc OPT -afoo -bc`           | 1回目 `a`, 2回目 `b`, 3回目 `c` | 1回目 `foo` | 3      |
| `getopts abc OPT -a foo -b bar -c`     | 1回目 `a`                       |             | 2      |
| `getopts a:bc OPT -a foo -b bar -c`    | 1回目 `a`, 2回目 `b`            | 1回目 `foo` | 4      |

※ 全てのオプション解析が終わった時（= `getopts` が非0を返した時）に `OPT` 変数に代入される値は POSIX 仕様では未定義のようです。殆どのシェルでは `?` になりますが zsh では最後に読み取ったオプションでした。

### getopts の機能

`getopts` の使用法方は以下のとおりです。

```
getopts optstring name [args]
```

オプション文字列の意味は、オリジナル版の `getopt` と同じなので省略します。第二引数は変数名で読み取ったオプションが代入されます。`args` は `getopts` に解析させたい引数で省略された場合は現在の位置パラメータ `"$@"` を指定したのと同じ意味になります。

※ [getopts の仕様](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/getopts.html) によるとオプション文字列に使用できる文字は英数字のみで、その他の文字を使用した場合の結果は不定です。(`The use of other option characters that are not alphanumeric produces unspecified results.`) `getopts` でロングオプションに対応させる方法としてオプション文字列に `-` を含めるやり方がありますが、POSIX 的には好ましくありません。


### エラーメッセージ

`getopts` では独自のエラーメッセージに変更することが可能です。エラーメッセージ抑制する方法は `getopt` と同じくオプション文字列の先頭を `:` にします。エラーが発生すると、エラーメッセージは抑制され （`getopt` の場合とは違い）終了ステータスは `0` となります。発生したエラーが「不正なオプション」の場合は第二引数で指定した変数に `?` が代入され、`OPTARG` 変数に不正なオプション文字が代入されます。発生したエラーが「オプション引数がない」場合は第二引数で指定した変数に `:` が代入されます。

| 呼び出し             | OPT | OPTARG |
| -------------------- | --- | ------ |
| `getopts :a OPT -x`  | `?` | `x`    |
| `getopts :a: OPT -a` | `:` |        |

※ bash では `OPTERR` 変数に `0` を設定してもエラーメッセージを抑制することができます。`OPTERR` 変数は POSIX の初期の提案でなされたものですが `:` を指定する方法に置き換わり bash 以外は対応していません。

### + で始まるオプション

zsh, mksh, ksh では `+` で始まるオプションを受け付けることが出来ますが、これは POSIX の仕様ではありません。

zsh と mksh では `+` で始まるオプションを受け付けるために特にすることはありません。そのため、`+` で始まる（オプションではない）引数が意図せずオプションとして扱われてしまう可能性があるので注意してください。ksh ではオプション文字列の先頭（エラーメッセージを抑制する `:` がある場合はその次）を `+` にします。

| 呼び出し             | OPT (zsh, mksh) | OPT (ksh) |
| -------------------- | --------------- | --------- |
| `getopts ab OPT +b`  | `+b`            | `?`       |
| `getopts +ab OPT +b` | `+b`            | `+b`      |
| `getopts +ab OPT ++` | `++`            | `?`       |

### OPTIND 詳細

`getopts` は実行するたびにオプションを一つずつ処理していきます。つまりどこまで処理したかという状態を持っているわけです。その状態を持っている変数が `OPTIND` 変数です。`OPTIND` 変数に `1` を代入するとこの状態はリセットされ、新たなオプションを解析することができます。

さて以下のようなコードで、実行するたびに `OPTIND` 変数がどのように変わるか確認してみましょう。

```sh
echo "OPTIND:$OPTIND (start)"

set -- -a -bc -d data -e

while getopts abcd:e OPT; do
  echo "OPT:$OPT OPTIND:$OPTIND"
done

echo "OPT:$OPT OPTIND:$OPTIND (end)"
```

| 行番号 | 出力 (dash, mksh)      | 出力 (bash, ksh, posh) | 出力 (zsh)             | 出力 (yash)            |
| ------ | ---------------------- | ---------------------- | ---------------------- | ---------------------- |
| 1      | `OPTIND:1 (start)`     | `OPTIND:1 (start)`     | `OPTIND:1 (start)`     | `OPTIND:1 (start)`     |
| 2      | `OPT:a OPTIND:2`       | `OPT:a OPTIND:2`       | `OPT:a OPTIND:1`       | `OPT:a OPTIND:1:2`     |
| 3      | `OPT:b OPTIND:3`       | `OPT:b OPTIND:2`       | `OPT:b OPTIND:2`       | `OPT:b OPTIND:2:2`     |
| 4      | `OPT:c OPTIND:3`       | `OPT:c OPTIND:3`       | `OPT:c OPTIND:2`       | `OPT:c OPTIND:2:3`     |
| 5      | `OPT:d OPTIND:5`       | `OPT:d OPTIND:5`       | `OPT:d OPTIND:5`       | `OPT:d OPTIND:5`       |
| 6      | `OPT:e OPTIND:6`       | `OPT:e OPTIND:6`       | `OPT:e OPTIND:5`       | `OPT:e OPTIND:5:2`     |
| 7      | `OPT:? OPTIND:6 (end)` | `OPT:? OPTIND:6 (end)` | `OPT:e OPTIND:6 (end)` | `OPT:? OPTIND:6 (end)` |

なんとバラバラです。特に yash の出力はどういうことでしょうか？

`OPTIND` 変数の内容を次に処理すべきインデックス番号と仮定すると、`-a -bc -d data -e` という引数の場合に、1番目の引数 `-a` を処理するときは `1` ですが、さて2番目の引数のうち `-c` を処理するときはどうなるべきでしょうか？

つまりショートオプションが結合されてる場合ではインデックス番号の数値一つだけでは足りないのです。そのため yash では `:` 区切りで二つの数値を `OPTIND` 変数に持っているわけです。他のシェルでは内部に見えない状態変数を持っているようです。

[getopts](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/getopts.html) によると、`OPTIND` 変数は、`getopts` が非0で終了した時に、オプションを除くの最初の引数のインデックス番号になるとしか規定されてないようです。つまり途中の値がそれぞれ違っていても POSIX 仕様上は何の問題もなさそうです。（話がそれますが、zshの7行目の出力 `OPT` の値は `e` ですが POSIX ではオプション解析処理が終了したときに `?` にするとなってるので POSIX 準拠してないようです。）

さて、上の方で「`OPTIND` 変数に `1` を代入するとリセットされる。」と書きました。その検証をしてみます。

```sh
set -- -o -x # パターン1
# set -- -ox # パターン2
getopts ox OPT1
OPTIND=1
getopts ox OPT2
echo "$OPT1 $OPT2" # すべて o o と表示されるはず
```

| パターン  | 出力 (dash, bash, ksh, mksh, yash) | 出力 (zsh) | 出力 (posh) |
| --------- | ---------------------------------- | ---------- | ----------- |
| パターン1 | `o o`                              | `o x`      | `o o`       |
| パターン2 | `o o`                              | `o x`      | `o x`       |

zsh と posh ではリセットされませんでした。POSIX に準拠していないということでしょうか？ zsh や posh では `OPTIND` 変数を `unset` することで状態をリセットできましたが、`unset` すると、`dash` でエラーになりました。`0` を代入すると yash でおかしくなります。どのシェルでも動く汎用的なリセット方法は見つからなかったのでシェル毎にリセット方法を変えるしかなさそうです。

### 使用方法

もっともシンプルな例はこんな感じでしょうか？

```sh
while getopts a:b OPT; do
  case $OPT in
    a) A_VALUE=$OPTARG ;;
    b) B_FLAG=1 ;;
    *) exit 1 ;; # 不正なオプション または オプション引数がない場合（OPT = ?）
  esac
done
shift $((OPTIND - 1))
```

`getopt` と比べて簡単になっている点は、事前の引数の整形が必要ないので位置パラメータへの設定が不要であるのと、オプション引数を飛ばす処理を `getopts` が行うのでオプション毎の `shift` が不要になっている点です。代わりにオプション解析でエラーが発生したときの中断処理と残りの引数を処理するための `shift $((OPTIND - 1))` が増えています。

上記のコードは、オプション解析でエラーが発生した時のメッセージを `getopts` にまかせていますが、メッセージをカスタマイズしたい場合は以下のようになります。エラーメッセージを抑制すると `OPT` 変数に入っている文字でエラーの内容を区別することができます。

```sh
while getopts :a:b OPT; do
  case $OPT in
    a) A_VALUE=$OPTARG ;;
    b) B_FLAG=1 ;;
    :) echo "Mission arg: -$OPTARG" >&2; exit 1 ;; # オプション引数がない (OPT = :)
    *) echo "Unknown option: -$OPTARG" >&2; exit 1 ;; # 不正なオプション (OPT = ?)
  esac
done
shift $((OPTIND - 1))
```

## getopt (GNU版)

GNU 版の `getopts` の使用法方は以下のとおりです。

1. `getopt optstring parameters`
2. `getopt [options] [--] optstring parameters`
3. `getopt [options] -o|--options optstring [options] [--] parameters`

1 はオリジナル版の `getopt` に近い動きとなり、**引数に空白や特殊文字が使えません。** 2, 3 が GNU で拡張された動きになります。個人的には 2 は直感的でないと感じるので、3 をおすすめします。

GNU 版の `getopt` による出力例を以下に示します。

| 呼び出し                                | 出力                             |
| --------------------------------------- | -------------------------------- |
| `getopt -o abc -- -a -b -c`             | ` -a -b -c --`                   |
| `getopt -o abc -- -abc`                 | ` -a -b -c --`                   |
| `getopt -o abc -- -a -b -c foo bar baz` | ` -a -b -c -- 'foo' 'bar' 'baz'` |
| `getopt -o a:bc -- -afoo -bc`           | ` -a 'foo' -b -c --`             |
| `getopt -o abc -- -a foo -b bar -c`     | ` -a -b -c -- 'foo' 'bar'`       |
| `getopt -o a:bc -- -a foo -b bar -c`    | ` -a 'foo' -b -c -- 'bar'`       |

### GNU版 getopt の機能

#### ロングオプション

`-l` または `--longoptions` でロングオプションを指定できます。ロングオプションはカンマ区切りで複数指定できます。ロングオプションだけを指定する場合でも、ショートオプションを指定する `-o` または `--options` は省略できないことに注意してください。

| 呼び出し                                             | 出力                    | 補足                           |
| ---------------------------------------------------- | ----------------------- | ------------------------------ |
| `getopt -o '' -l long-a,long-b -- --long-a --long-b` | ` --long-a --long-b --` |                                |
| `getopt -o '' -l reverse,recursive -- --rev`         | ` --reverse --`         | オプション名は短縮指定できます |

#### ロングオプションのオプション引数

ショートオプションと同様に、オプション名の最後に `:` をつけることで指定します。オプション引数はスペースで区切る他、オプションに `=` でつないで渡します。

| 呼び出し                                       | 出力                      |
| ---------------------------------------------- | ------------------------- |
| `getopt -o '' -l long-a: -- --long-a foo`      | ` --long-a 'foo' --`      |
| `getopt -o '' -l long-a: -- --long-a --long-a` | ` --long-a '--long-a' --` |
| `getopt -o '' -l long-a: -- --long-a=foo`      | ` --long-a 'foo' --`      |

#### 省略可能なオプション引数

省略可能なオプション引数は、オプション名の最後に `::` をつけることで指定します。オプション引数はショートオプションにつなげて渡すか、ロングオプションに `=` でつないで渡します。（スペースで区切って渡すことはできません。）

| 呼び出し                                                   | 出力                             |
| ---------------------------------------------------------- | -------------------------------- |
| `getopt -o a::b -- -a -b`                                  | ` -a '' -b --`                   |
| `getopt -o a::b -- -a foo -b`                              | ` -a '' -b -- 'foo'`             |
| `getopt -o a::b -- -afoo -b`                               | ` -a 'foo' -b --`                |
| `getopt -o '' -l long-a::,long-b -- --long-a --long-b`     | ` --long-a '' --long-b`          |
| `getopt -o '' -l long-a::,long-b -- --long-a foo --long-b` | ` --long-a '' --long-b -- 'foo'` |
| `getopt -o '' -l long-a::,long-b -- --long-a=foo --long-b` | ` --long-a 'foo' --long-b --`    |

#### オプションとオプション以外の混在

GNU 版の `getopt` ではオプションとオプション以外を混在しても処理しやすい形に並び替えてくれます。途中でオプションとして `--` 見つかった場合はそこで並び替えを中断します。この機能を無効にしてオリジナル版の `getopt` と同じ動きにするためには、オプション文字列の最初に `+` を置くか、`POSIXLY_CORRECT` 変数を定義します。

オプション文字列の先頭を `-` にすると、オプションでない引数は（`--` の後ろのまとめられるのではなく）そのままの位置になります。（結合されたショートオプションの分解だけを行いたい時に使う？）

| 呼び出し                               | 出力                             |
| -------------------------------------- | -------------------------------- |
| `getopt -o abc -- -a foo -b bar -c`    | ` -a -b -c -- 'foo' 'bar'`       |
| `getopt -o +abc -- -a foo -b bar -c`   | ` -a -- 'foo' '-b' 'bar' '-c'`   |
| `getopt -o -abc -- -a foo -b bar -c`   | ` -a 'foo' -b 'bar' -c --`       |
| `getopt -o abc -- -a foo -b -- bar -c` | ` -a -b -- 'foo' 'bar' '-c'`     |
| `getopt -o abc -- -abc foo -b bar -c`  | ` -a -b -c -b -c -- 'foo' 'bar'` |
| `getopt -o '' -l long -- --long=foo`   | `  --long 'foo' --`              |

#### 単一の - で始まるロングオプション

 `-a` または `--alternative` オプションを指定すると有効になります。`find` コマンドで使われてる形式のオプションですね。（例 `find . -name "*.txt"`）`-` で始まるロングオプションは `--` で始まるロングオプションに整形されます。

| 呼び出し                                             | 出力                              |
| ---------------------------------------------------- | --------------------------------- |
| `getopt -o cetux -l exec -- -exec echo`              | ` -e -x -e -c -- 'echo'`          |
| `getopt -o cetux -l exec -a -- -exec echo`           | ` --exec -- 'echo'`               |
| `getopt -o cetux -l exec -a -- --exec echo`          | ` --exec -- 'echo'`               |
| `getopt -o cetux -l exec -a -- -execute echo`        | ` -e -x -e -c -u -t -e -- 'echo'` |
| `getopt -o 'c:e:t:u:x:' -l exec -a -- -exec echo`    | ` --exec -- 'echo'`               |
| `getopt -o 'c:e:t:u:x:' -l exec -a -- -execute echo` | ` -e 'xecute' -- 'echo'`          |

### 使用方法

ロングオプションなどの機能が追加されているだけで、使用方法はオリジナル版の `getopt` と殆ど変わりません。`getopts` に比べるとロングオプションが使えるなど機能は増えましたが、`getopts` のシンプルさはなくなりました。

```sh
# 位置パラメータへの設定
args=$(getopt -o a:b -l long-a:,long-b -- "$@") || exit 1 # $@ を使うこと。$* では空白を正しく扱えない
eval "set -- $args" # オリジナル版と違いクォートでくくられるので eval する必要がある

# オプション解析のためのループ
while [ $# -gt 0 ]; do
  case $1 in
    -a | --long-a) A_VALUE=$2; shift 2 ;;
    -b | --long-b) B_FLAG=1; shift ;;
    --) shift; break ;;
  esac
done
```

実際に書いてみるとすぐに気づくと思うのですが、ロングオプションの数が多くなると `getopt` の引数は長くなり `case` の部分とニ箇所に同じオプションを書かなければなりません。これは `getopt` が引数を整形する機能しかないからです。実際の引数の解釈は別の `case` で行わなければなりません。ショートオプションだけならまだマシですが、ロングオプションが増えてくると大変になってきます。

なお Ubuntu 18.04 の `man getopt` より使用例が書かれたファイルがあることがわかるのですが、正しいパスは ` /usr/share/doc/util-linux/examples/getopt-parse.bash` （`getopt-parse.tcsh`）でした。

> 例
>      (ba)sh  と (t)csh での使用例のスクリプトは、 getopt(1) ディストリビューションで提供されている。 
>  これらはオプションとして /usr/local/lib/getopt または /usr/lib/getopt にインストールされている。

## まとめ

以上、`getopt` と `getopts` について徹底的に解説してみましたが、結局私は使わないんですよね・・・。以下が私の方針です。

* オリジナル版（BSD版）の `getopt` は使用しません。
* ショートオプションだけの簡単なものであれば `getopts` を使用します。
* ロングオプションやその他の機能が欲しくなったら独自実装します。
* GNU版の `getopt` は特定の環境だけで動けばよく簡易なものを手っ取り早く作りたいときには使うかもしれません。

### 独自実装のオプション解析コード

`getopt`, `getopts` を使用しない独自実装のオプション解析コードについては 「[高機能で短いシェルスクリプト用のオプション解析コード（POSIXシェル準拠・独自実装）](https://qiita.com/ko1nksm/items/7d37852b9fc581b1266e) 」を参照してください。

**関連リンク**

* [bash によるオプション解析](https://qiita.com/b4b4r07/items/dcd6be0bb9c9185475bb)
* [シェルスクリプト オプション解析テンプレート](https://qiita.com/rita_cano_bika/items/9fcb2a61c6f360632541)
* [bash: コマンドライン引数のオプション解析(optgets)](https://qiita.com/laikuaut/items/adf35ecb14c55f6fde92)