引数処理

普通のコマンドと同じ様に、bashスクリプトでも引数を扱う事ができます。

今まで作成したスクリプトでスクリプトにオプションを付けて、位置パラメータで値を取得する方法を紹介していました。

これも立派に機能はしていますが、使用する側からすると不便です。

付与されたオプションの「位置」から値を取得する為、空オプションが許されないからです。

スクリプトによっては付与されるオプションによって複数の動作を使い分けたいと思いますが、そういう時には上記の理由により位置パラメータからオプションを取得するのはお勧めできません。

そういう時の為に、「getopts」というコマンドがあります。

このコマンドを使えば引数を利用でき、オプションを「多様」に「判りやすく」扱えます。

うまく利用しましょう。

 

「getopts」コマンドは以下の様にして使用します。

while getopts "引数種類を記述" 引数代入変数
do
    case ${引数代入変数} in
        引数1)
            引数1の時に行う処理
            ;;
        引数2)
            引数2の時に行う処理
            ;;
        *)
            上記以外の引数の場合の処理
            ;;
    esac
done

shift $(( ${OPTIND} -1 ))

 

これが基本形です。

複数の引数を想定している為、「while」ループで、引数がある限り取得し続けます。

実際に使用してみましょう。

#!/bin/bash

## 引数処理サンプル

while getopts "abcd" V_OPT
do
    case ${V_OPT} in
        a)
            echo "引数はaです"
            ;;
        b)
            echo "引数はbです"
            ;;
        c)
            echo "引数はcです"
            ;;
        d)
            echo "引数はdです"
            ;;
        *)
            echo "想定していない引数です"
            ;;
    esac
done

shift $(( ${OPTIND} -1 ))

exit 0

 

早速、実行してみます。

# ./option01.sh 
# ./option01.sh -a
引数はaです
# ./option01.sh -b
引数はbです
# ./option01.sh -a -b
引数はaです
引数はbです
# ./option01.sh -ab
引数はaです
引数はbです
# ./option01.sh -ca
引数はcです
引数はaです
# ./option01.sh -e
./option01.sh: illegal option -- e
想定していない引数です
# 

 

5番目の実行例までは、ちゃんと引数が処理されてますね。

6番目の実行例では、想定されていない引数を付けて実行してみましたが、case文で定義されている文が出力される前に、「optgets」コマンド自体が

「不正な引数」としてエラーを出力しています。

デバッグの時などは、こういうエラーを出してくれるのは嬉しいのですが、実使用で出されると、ちょっと目障りです。この出力を抑止してしまいましょう。

抑止するには、引数種類を書く先頭部分で「:」をつけます。

以下の様に書き直しました。

#!/bin/bash

## 引数処理サンプル

while getopts ":abcd" V_OPT
do
    case ${V_OPT} in
        a)
            echo "引数はaです"
            ;;
        b)
            echo "引数はbです"
            ;;
        c)
            echo "引数はcです"
            ;;
        d)
            echo "引数はdです"
            ;;
        *)
            echo "想定していない引数です"
            ;;
    esac
done

shift $(( ${OPTIND} -1 ))

exit 0

 実行してみます

# ./option02.sh -a
引数はaです
# ./option02.sh -e
想定していない引数です
# ./option02.sh -aed
引数はaです
想定していない引数です
引数はdです
# ./option02.sh -eb
想定していない引数です
引数はbです
# 

 

これでエラーも吐かれなくなりました。

ところがこれでも引数処理としては不完全です。

このコマンドに引数を付けないで実行してみましょう。

# ./option02.sh 
# ./option02.sh aaa
# 

 

引数が付いてない場合、「getopts」コマンドでは認識してくれません。

「getopts」コマンドは引数以外のオプションに関しては関与してくれないからです。

ですが、スクリプトを実行する人はそんな事はお構いありませんので、作成する側は、こういう部分に関しても考慮が必要です。

「getopts」で定義されている以外のオプションが付いていた場合、エラーを出力する事にしましょう。

#!/bin/bash

## 引数処理サンプル

if [ "$1" = "" ] 
then
    echo "引数がありません"
fi

while getopts ":abcd" V_OPT
do
    case ${V_OPT} in
        a)
            echo "引数はaです"
            ;;
        b)
            echo "引数はbです"
            ;;
        c)
            echo "引数はcです"
            ;;
        d)
            echo "引数はdです"
            ;;
        *)
            echo "想定していない引数です"
            ;;
    esac
done

shift $(( ${OPTIND} -1 ))

if [ "$1" != "" ] 
then
    echo "$1は想定していない引数です"
fi

exit 0

 実行してみましょう

# ./option03.sh 
引数がありません
# ./option03.sh -e
想定していない引数です
# ./option03.sh aaa
aaaは想定していない引数です
# 

 

これでどの様な引数が付いていても処理してくれるでしょう。

エラー判定の処理については後述しますが、若干面倒な事をやっています。

慣れるまでは「この形が定番」という覚え方でいいと思います。

あとは、不正な引数処理の時にエラーメッセージだけでなく、このコマンドの使い方を書いたusageを出力してあげる事で、使う人にも優しいスクリプトになります。

#!/bin/bash

## 引数処理サンプル

if [ "$1" = "" ] 
then
    echo "引数がありません"
    echo "USAGE : `basename $0` [-abcd]"
    exit 1
fi

while getopts ":abcd" V_OPT
do
    case ${V_OPT} in
        a)
            echo "引数はaです"
            ;;
        b)
            echo "引数はbです"
            ;;
        c)
            echo "引数はcです"
            ;;
        d)
            echo "引数はdです"
            ;;
        *)
            echo "想定していない引数です"
            echo "USAGE : `basename $0` [-abcd]"
            exit 1
            ;;
    esac
done

shift $(( ${OPTIND} -1 ))

if [ "$1" != "" ] 
then
    echo "$1は想定していない引数です"
    echo "USAGE : `basename $0` [-abcd]"
    exit 1
fi

exit 0

 

実行するとこうなります。

# ./option04.sh 
引数がありません
USAGE : option04.sh [-abcd]
# ./option04.sh -e
想定していない引数です
USAGE : option04.sh [-abcd]
# ./option04.sh aaa
aaaは想定していない引数です
USAGE : option04.sh [-abcd]
# 

 

「getopts」コマンドは引数だけでなく、引数に付加する形で文字列を渡す事もできます。

文字列を渡したい場合は「getopts」コマンドの引数種類を定義する際に、引数の後ろに「:」を付加するだけです。

上記のスクリプトで、「-c」オプションには文字列を付けたい場合、引数種類を定義する際にこう書きます。

getopts ":abc:d" V_OPT

 

そして付加された文字列を取り出す場合は、特殊変数「OPTARG」を使用します。

では、書き直してみましょう。

#!/bin/bash

## 引数処理サンプル

if [ "$1" = "" ] 
then
    echo "引数がありません"
    echo "USAGE : `basename $0` [-abcd]"
    exit 1
fi

while getopts ":abc:d" V_OPT
do
    case ${V_OPT} in
        a)
            echo "引数はaです"
            ;;
        b)
            echo "引数はbです"
            ;;
        c)
            echo "引数はcです"
            echo "引数に付加された文字は${OPTARG}です"
            ;;
        d)
            echo "引数はdです"
            ;;
        *)
            echo "想定していない引数です"
            echo "USAGE : `basename $0` [-abcd]"
            exit 1
            ;;
    esac
done

shift $(( ${OPTIND} -1 ))

if [ "$1" != "" ] 
then
    echo "$1は想定していない引数です"
    echo "USAGE : `basename $0` [-abcd]"
    exit 1
fi

exit 0

 

実行するとこうなります

# ./option05.sh -a
引数はaです
# ./option05.sh -c aaa
引数はcです
引数に付加された文字はaaaです
# ./option05.sh -c aaa -b
引数はcです
引数に付加された文字はaaaです
引数はbです
# ./option05.sh -ac aaa -b
引数はaです
引数はcです
引数に付加された文字はaaaです
引数はbです
# ./option05.sh -c
想定していない引数です
USAGE : option05.sh [-abcd]
#

 

「-c 文字列」という形で文字列を渡す事ができるようになりました。

コマンドの使用方法が変わったので、合わせてusageも書き換えておきましょう。

「getopts」コマンドの使い方はだいたい掴めましたか?

さりげなくスクリプト内で使っていますが、いくつかこのコマンドに関する特殊変数が存在します。それも説明しておきましょう。

OPTERR      getoptsコマンドのエラー表示オプション
OPTARG      引数についたオプションを表示
OPTIND      「getopts」コマンドで処理する引数の数を表示

 

「OPTERR」は、最初に説明した、引数種類の前につける「:」と同じです。

この特殊変数に「0」を設定する事で、エラー表示を抑止する事が可能となります。

「OPTARG」は上記のスクリプトの様に、引数に付加されたオプションを格納します。

ひとつの引数処理を行う度にその引数に付加されたオプションで上書きされますので、値を取り出したらすぐに別の変数へコピーするか、その場で処理をしてしまいましょう。

「OPTIND」は「getopts」コマンドで処理した引数個数を表します。デフォルト値は「1」になります。

上記スクリプトの「while」ループの後で行っている

shift $(( ${OPTIND} -1 ))

 

というのは、全オプション個数から、「getopts」コマンドで引数処理を行ったオプション個数を引く事によってshiftコマンドで位置パラメータを戻している処理です。

位置パラメータを戻す事によって、その直後のif文により「getopts」コマンドで処理をしていないオプションが存在するかを「$1」に文字列が格納されているかによって判断する事ができます。

# ./option05.sh 
引数がありません
USAGE : option05.sh [-abcd]
# ./option05.sh -a
引数はaです
# ./option05.sh -a aaa
引数はaです
aaaは想定していない引数です
USAGE : option05.sh [-abcd]
# 

 

この一連の処理で説明すると

# ./option05.sh 

 

この場合、全オプション個数は1個(スクリプト自身がカウントされる)ですが、オプション1個目は存在しません。

その為、一番最初のif文により、オプションが付いていない事を判定してエラーになります。

# ./option05.sh -a

 

この場合、引数個数は2個、「OPTIND」はデフォルトで「1」が格納されてますので、「-a」を処理した分だけカウントアップして「2」が最終的に格納されます。

この「2」から1マイナスされた分だけshiftされますので、「1」shiftされて「-a」の後ろから位置パラメータが有効になります。

その為、以下の処理の場合、

# ./option05.sh -a aaa

 

「-a」の後ろにある「aaa」という文字列から位置パラメータが有効になり、最後のif文で文字列が格納されていると判定されてエラーとなります。

ここで実際にスクリプトを書いて「OPTIND」の動きを説明をしてもいいのですが、敢えてやりません。

初心者向け講座で説明する内容でもないからです。

上の説明をいまいち理解できなかった人は、とりあえず引数処理の定番の形だけを覚えておきましょう。

どうしても「OPTIND」に格納される値の動きを見たい人は、「$#」と一緒に値を表示しながら処理を観察すると判りやすいかと思います。