【bash】getoptsでコマンドの引数を取得するsh(ショートオプションとロングオプション対応)

# 1. 概要
シェルスクリプトで引数を取得する際、getoptsコマンドを使用するのが一般的かと思いますが、getoptsコマンドは普通に使用するだけだとロングオプションに対応できません。
ロングオプションに対応させるには独自のロジックを組む必要があります。

本記事では、getoptsコマンドを使用したショートオプションとロングオプションに対応したシェルスクリプトのデモコードを公開します。

# 2. スクリプト
本題のスクリプトは下記の通りとなります。

```bash:get_arguments_demo.sh
#!/bin/bash

### Function:help&エラー出力 ###
function usage {
  cat <<EOM
Usage: $(basename "$0") [OPTION]...
  -h/--help   -       Display help
  -s/--string VALUE   output the character string argument
  -n/--num    VALUE   output the character number argument
  
hint: Do not put a space between the option and the argument.
hint: You can specify more than one of the same option.
hint: Correct execution example.

  $(basename "$0") [OPTION] [ARGMENT]

EOM
  exit 1
}

### メイン処理 ###
# i=0:処理が実行されなかった
# i=1:処理が実行された
i=0
# 引数を取る指定は「-」のみ
while getopts snh-: opt; do
  # 処理が開始したことを確認
  export i=1
  # OPTIND 番目の引数を「optarg」へ代入
  optarg="${!OPTIND}"
  # オプションと引数の間の空白行を禁止する
  if [ -z "$optarg" ];then usage ;fi
  if [ ${optarg:0:1} = "-" ];then usage ;fi
  # ロングオプション用に整形
  [[ "$opt" = - ]] && opt="-$OPTARG"
  # 引数を取得
  case "-$opt" in
    -s|--string)
      string="STRING=$optarg"
      shift
      echo $string
      ;;
    -n|--num)
      num="NUMBER=$optarg"
      echo $num
      shift
      ;;
    -h|--help|*)
      usage
      ;;
  esac
done 2>/dev/null
# オプションをすべて削除し、引数だけ残す
shift $((OPTIND - 1))
# 正しい引数が指定されているか判定
if [ $i -ne 1 ];then usage unset i; else unset i;fi
```

# 3. sh実行例
## 3.1 実行例
```bash:実行例
$ ./get_arguments_demo.sh -s HelloWorld
STRING=HelloWorld

$ ./get_arguments_demo.sh -s HelloWorld -n 100                                
STRING=HelloWorld
NUMBER=100

$ ./get_arguments_demo.sh --string HelloWorld
STRING=HelloWorld

$ ./get_arguments_demo.sh --string HelloWorld --num 100
STRING=HelloWorld
NUMBER=100

$ ./get_arguments_demo.sh -s HelloWorld --num 100                              
STRING=HelloWorld
NUMBER=100
```
## 3.2 エラー出力
基本的にはエラー時は用意したhelp処理を出力させます。
※shの組み込みエラーは、2>/dev/nullに飛ばします

```bash:エラーパターン
# 未知のオプション、及び引数を指定した場合
$ ./get_arguments_demo.sh -a HelloWorld
$ ./get_arguments_demo.sh --stringg HelloWorld
$ ./get_arguments_demo.sh -
$ ./get_arguments_demo.sh abc
# オプションと引数の間を詰めた場合
$ ./get_arguments_demo.sh -sHelloWorld
$ ./get_arguments_demo.sh --num100
# オプション、及び引数を指定しない場合
$ ./get_arguments_demo.sh
# -h/--helpオプションを指定した場合
$ ./get_arguments_demo.sh -h
$ ./get_arguments_demo.sh --help
```

↓ 上記コマンドを実行した場合、下記エラーを出力

```bash:エラー出力
Usage: get_arguments_demo.sh [OPTION]...
  -h/--help   -       Display help
  -s/--string VALUE   output the character string argument
  -n/--num    VALUE   output the character number argument
  
hint: Do not put a space between the option and the argument.
hint: You can specify more than one of the same option.
hint: Correct execution example.

  get_arguments_demo.sh [OPTION] [ARGMENT]

```
# 参照
- [shell scriptでlong optionを処理する](https://www.usinezumi.com/blog/2016/10/16/304/)
- [bashでロングオプションとショートオプションの両方に対応する](https://qiita.com/akameco/items/0e932d8ec372b87ccb34)
- [シェルスクリプトでオプション付きのコマンドを作成する](https://qiita.com/Esfahan/items/e88bb806c7ca1dc8b758#%E3%83%91%E3%82%B9%E3%83%AF%E3%83%BC%E3%83%89%E3%83%97%E3%83%AD%E3%83%B3%E3%83%97%E3%83%88)
- [Bash の getopts でロングオプションを処理する](https://chitoku.jp/programming/bash-getopts-long-options)