シェルスクリプトのオプション解析の書き方

この記事では、シェルスクリプト(bash)における、
getoptsを使った、オプション解析の書き方について紹介します。

現職でサーバーサイドエンジニアをしている筆者ですが、
シェルスクリプトは結構な頻度で書くことが多いにも関わらず、
いつも書き方は覚えてないので、自分のブログにメモとして記載しておくことにします。

getoptsを使ったオプション解析

オプション解析が必要なシェルスクリプトに使っているテンプレートはこちら。

#!/bin/bash
set -u

usage() {
    echo "
usage: 
    $0 [-h] [-a] [-b value] [-c] <FILE1> <FILE2>
" >&2
}

# オプション変数の初期値
flag_a=false
value_b=
flag_c=false

# オプション解析
while getopts ab:ch OPT; do
    case $OPT in
    a) 
        flag_a=true
        ;;
    b) 
        value_b="$OPTARG"
        ;;
    c) 
        flag_c=true
        ;;
    h | \?) 
        usage && exit 1
        ;;
    esac
done

# コマンドライン引数から、オプション引数分を削除
shift $((OPTIND - 1))

if $flag_a; then
    echo "aフラグは指定されています!"
else
    echo "aフラグは指定されていません。"
fi

if [[ -n "$value_b" ]]; then
    echo "b値は指定されています! ($value_b)"
else
    echo "b値は指定されていません。"
fi

if $flag_c; then
    echo "cフラグは指定されています!"
else
    echo "cフラグは指定されていません。"
fi

echo "残りのコマンドライン引数: n=$# args=($@)"

if [[ $# -ne 2 ]]; then
    usage
    exit 1
fi

echo "do something."

処理内容としては、

  • a, b, c, h のオプション引数を受け取れるようにしています。
  • -h もしくは、不正なフラグが指定された場合、usageを出力して終了する。
  • bオプションは、オプション引数を取るよう定義している。
  • 最後にオプションじゃないメイン引数が2つあるか確認してます。

このシェルスクリプトに実行権限を付与して実行してみるとこんな感じ。

$ ./test.sh -h

usage:
    ./test.sh [-h] [-a] [-b value] [-c] <FILE1> <FILE2>

$ ./test.sh file1.txt file2.txt
aフラグは指定されていません。
b値は指定されていません。
cフラグは指定されていません。
残りのコマンドライン引数: n=2 args=(file1.txt file2.txt)
do something.

$ ./test.sh -a -b 10 -c file1.txt file2.txt
aフラグは指定されています!
b値は指定されています! (10)
cフラグは指定されています!
残りのコマンドライン引数: n=2 args=(file1.txt file2.txt)
do something.

getoptsのusage

getopts optstring name [argument]...

optstringに、ショートオプションで指定できる文字を列挙します。
illegal option のエラーを出したくない場合は、optstringの先頭に :(コロン) を記載すると抑止できます。

nameは、解析したオプション文字が代入される変数を指定します。

オプションフラグ

# オプション解析
while getopts ab:ch OPT; do
    case $OPT in
    a) 
        flag_a=true
        ;;
    b) 
        value_b="$OPTARG"
        ;;
    c) 
        flag_c=true
        ;;
    h | \?) 
        usage && exit 1
        ;;
    esac
done

while getopts ab:ch OPT; do
getopsに指定している”ab:ch” の部分から解説します。

ここに指定している文字がフラグとして解析されます。
この場合、a, b, c, h をオプションとしてgetoptsに解析依頼していることになります。

OPTは、解析後のフラグ文字が代入される変数で、
while文で回して、case文で一つずつ処理する流れになっています。

今回指定している、a,b,c,h 以外のオプションを指定した場合、
getoptsは、”illegal option”というエラーメッセージを出力します。

そしてcase文では、 \? にマッチします。

$ ./test.sh -d
./test.sh: illegal option -- d

usage:
    ./test.sh [-h] [-a] [-b value] [-c] <FILE1> <FILE2>

オプション引数

引数を取るオプションも定義ができます。

while getopts ab:ch OPT; do
“b:”の部分がそれを意味しています。

引数をとるオプション文字の後ろに:(コロン)をつける。
引数をとるオプションの場合、値はOPTARG変数に代入されています。

 b)
     value_b="$OPTARG"
   ;;

残りのコマンドライン引数

オプション解析をした後は、コマンドラインに指定した引数変数はそのままなので、
以降の処理で扱いやすいように、shiftコマンドを使って、切り詰めておくのが便利です。

shift $((OPTIND - 1))

OPTIND変数は、getoptsが自動的に代入している変数で、
次に処理するコマンドライン引数のインデックス値を保持しています。

shiftコマンドを使って、オプションとして処理されたコマンドライン引数を切り詰めることができます。

まとめ

getopts は、POSIX準拠シェルの組み込みコマンドです。

ちょっとややこしいのが、似た者として、 getopt というのもありまして、こちらは外部コマンドで、使い方も全然異なります。
今回の記事では、bashの組み込みコマンドである getopts について紹介しました。

  • getopts ... POSIX準拠のシェルの組み込みコマンド
  • getopt ... 外部コマンド、高機能。

getopt のほうが、高機能ですが環境依存が多くなるのと、
シェルでやりくりする分には困らないので、私は getopts を好んで使っています。

あと、 getopts では、ロングオプションなどは使えませんのでご注意を。
(ショートオプションのみ)

もし、複雑なオプション解析が必要な処理の場合は、シェルスクリプトでやるんじゃなくて、
rustとかgoでCLI実装するのが良い選択だと思います。

タイトルとURLをコピーしました