ShellScript
Bash
Linux
UNIX
shell

getoptsを関数(function)の中で使う

基数変換について調べた

https://qiita.com/macoril/items/77ba050cad17f03ed46a

ついでに基数変換用のエイリアスを設定しようとして詰まった。


TL;DR

functionの中でgetoptsを使いたいときは

OPTOPTINDなどの変数をlocalで宣言しないといけない


詳細


修正前のコード

# 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
}



期待値と実際の結果


期待値

cbase -i 16 -o 10 A && cbase -i 16 -o 8 A && cbase -i 8 -o 10 16

10 12 14


実際の結果

$ cbase -i 16 -o 10 A && cbase -i 16 -o 8 A && cbase -i 8 -o 10 16

10 10 22


調査

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


デバッグ用

〜中略〜

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


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


デバッグ

$ 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で確認するとシェル変数として登録されていることがわかる。


変数を確認

$ env | egrep "^OPT|^input|^output" # 環境変数か

$ set | egrep "^OPT|^input|^output" # シェル変数か
OPT='?'
OPTERR=1
OPTIND=5
input=16
output=10

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


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で宣言する。


修正後のコード

〜中略〜

USAGE
}

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



修正後の実行結果

$ 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


参考