getoptsを関数（function）の中で使う

基数変換について調べた
https://qiita.com/macoril/items/77ba050cad17f03ed46a
ついでに基数変換用のエイリアスを設定しようとして詰まった。

# TL;DR

`function`の中で`getopts`を使いたいときは
`OPT`や`OPTIND`などの変数を`local`で宣言しないといけない

# 詳細

```sh:修正前のコード
# copy to your bash_profile or somewhere you write aliases,
# then you can convert a number to different base by cbase
# エイリアスを設定しているあたりにでも以下のコードをコピペすると基数変換がcbaseコマンドで簡単できるようになります
function cbase() {
  function echo_usage() {
    cat << USAGE
usage: cbase [-i num] [-o num] num
  -i num
    Set a number of base you want to convert FROM.
    Default is 10.
  -o num
    Set a number of base you want to convert TO.
    Default is 10.
USAGE
  }

  while getopts "i:o:" OPT; do
    case "$OPT" in
      i) input="$OPTARG" ;;
      o) output="$OPTARG" ;;
    esac
  done
  shift $(($OPTIND - 1))

  [ -z "$input" -a -z "$output" ] && echo_usage

  [ -z "$input"  ] && input=10
  [ -z "$output" ] && output=10
  echo "obase=$output; ibase=$input; $1" | bc
}
```

## 期待値と実際の結果

```sh:期待値
cbase -i 16 -o 10 A && cbase -i 16 -o 8 A && cbase -i 8 -o 10 16
10 12 14
```

```sh:実際の結果
$ cbase -i 16 -o 10 A && cbase -i 16 -o 8 A && cbase -i 8 -o 10 16
10 10 22
```

## 調査

変数周りが怪しいので以下のようにechoを仕込んで調査する。

```sh:デバッグ用
〜中略〜
  while getopts "i:o:" OPT; do
    case "$OPT" in
      i) input="$OPTARG" ;;
      o) output="$OPTARG" ;;
    esac
    echo "OPT:$OPT input:$input output:$output"
  done
  echo "OPTIND:$OPTIND input:$input output:$output"
  shift $(($OPTIND - 1))
  echo "OPTIND:$OPTIND input:$input output:$output"

  [ -z "$input" -a -z "$output" ] && echo_usage && return -1
```

実行してみると以下のように変数の値が引き継がれてしまっている。

```sh:デバッグ
$ cbase -i 16 -o 10 A
OPT:i input:16 output:
OPT:o input:16 output:10
OPTIND:5 input:16 output:10
OPTIND:5 input:16 output:10
10
$ cbase -i 16 -o 8 A
OPTIND:5 input:16 output:10
OPTIND:5 input:16 output:10
10
$ cbase -i 8 -o 10 16
OPTIND:5 input:16 output:10
OPTIND:5 input:16 output:10
22
```

setで確認するとシェル変数として登録されていることがわかる。

```sh:変数を確認
$ env | egrep "^OPT|^input|^output" # 環境変数か
$ set | egrep "^OPT|^input|^output" # シェル変数か
OPT='?'
OPTERR=1
OPTIND=5
input=16
output=10
```

これらをunsetしてから実行してみると以下のように実行結果が変わった。

```sh:unset後の実行結果
$ unset input output
$ cbase -i 8 -o 10 16
OPTIND:4 input: output:
OPTIND:4 input: output:
usage: cbase [-i num] [-o num] num
  -i num
    Set a number of base you want to convert FROM.
    Default is 10.
  -o num
    Set a number of base you want to convert TO.
    Default is 10.
$ unset OPT OPTIND
$ cbase -i 8 -o 10 16
OPT:i input:8 output:
OPT:o input:8 output:10
OPTIND:5 input:8 output:10
OPTIND:5 input:8 output:10
14
```

シェル変数として引き回されているのが問題っぽいので使用する変数をlocalで宣言する。

```sh:修正後のコード
〜中略〜
USAGE
  }

  local OPTIND OPT input output
  while getopts "i:o:" OPT; do
    case "$OPT" in
      i) input="$OPTARG" ;;
      o) output="$OPTARG" ;;
    esac
  done
〜中略〜
```

```sh:修正後の実行結果
$ cbase -i 16 -o 10 A && cbase -i 16 -o 8 A && cbase -i 8 -o 10 16
OPT:i input:16 output:
OPT:o input:16 output:10
OPTIND:5 input:16 output:10
OPTIND:5 input:16 output:10
10
OPT:i input:16 output:
OPT:o input:16 output:8
OPTIND:5 input:16 output:8
OPTIND:5 input:16 output:8
12
OPT:i input:8 output:
OPT:o input:8 output:10
OPTIND:5 input:8 output:10
OPTIND:5 input:8 output:10
14
```

# 完成版

問題なさそうなのでechoを削除して完成したのがこちら
https://gist.github.com/macoril/2cd76be7577507296a5f8219fe0904a9


# 参考

- https://qiita.com/b4b4r07/items/dcd6be0bb9c9185475bb
- https://stackoverflow.com/questions/16654607/using-getopts-inside-a-bash-function