それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

bash のシェルスクリプト(関数)でオプション引数を扱う getopts 使い方サンプル

シェルスクリプトで引数オプションを使いたい

my_shell_script -a /usr/bin

みたいに、bashシェルスクリプトでも引数を扱いたい。

getopts を使えば引数が取れる

bash では getopts を使えば、引数の処理を簡単に行うことができて便利。ruby などでは optparseに相当するやつです。

はじめに getopt / getopts の違いについて

全ての初めに。

getoptsについて読む前に知っておかなくちゃいけないことがある。

getopts コマンドとシェルのビルトインの二種類がある。

名前 種類
getopts シェル組み込み
getopt コマンド・ファイル

Google検索はgetopt/getopts を区別しないで結果を出すので注意が必要でした。

今回、調べたのは getopts です。

getopt は/usr/bin にあるコマンドです。

/usr/bin/getopt は、BSDOSX)とGNUlinux )で挙動が異なるため、OSXLinuxでどちらでも使えるようにするため、gnu bash の組み込み関数を使うことにしました。

getopts はシェル組み込み関数です。

今回扱うのは、getoptsシェル組み込み関数です。

getopts の基本的な使い方

getopts の基本的な使い方 は次の通りです。

getopts "引数名" 変数名
使用例

使い方は簡単です。

getopts "a" opts

引数に -a を指定したら opts 変数に中身(a)が入る

複数オプションを入れる

複数書きたいときは、連続してアルファベットを続ける

getopts "abc" opts

オプションで使いたいアルファベットを複数続けて書くだけ。楽チン。

実際に使って試してみる。

適当に関数を作ってgetopts を使ってみます。

function my_func(){
  echo 引数=$1
  getopts "adh" opts
  echo getopts結果=$opts
}

my_func -a
実行結果
引数=-a
getopts結果=a
未知の引数の場合
my_func -b
実行結果
引数=-a
/Users/takuya/Desktop/test.sh: illegal option -- b
getopts結果=?

何も書かなくても未指定の引数にはエラーを出してくれる。便利!

シェルの言語がja_JPであれば

takuya@~$ my_func -x
-bash: 不正なオプションです -- x

エラーメッセージをローカライズして表示してくれる。いいね

エラー処理を自分でやる?

組み込みのエラー処理はいらないと思う場合、

illegal option –を無視するには

先頭にセミコロンを置く
getopts ":adh" opts

こうすると、エラーを自分でハンドリングできる。

複数オプション の例

先ほどの関数に複数オプションをつけてみると。。。

my_func -a -d
引数=-a -d
getopts結果=a

複数オプションは考慮されない。 (後述するが、複数回getoptsを呼び出すことで処理できる。)

オプションを指定しない場合

getopts を設定した関数へ、なにもオプションを指定せずに起動すると・・・

my_func
引数=
getopts結果=?

結果は ? になる。

そのため、"?“ を判定してヘルプを表示するようスクリプトを記述する。

”?”(未知=ヘルプ)などと覚えておけばいいでしょう。

-a オプションではなく、ただの引数を入れる

ハイフン付きのオプションでななく、よくある引数を入れてみるとどうなるか。

my_func aaaaaaaaa
引数=aaaaaaaaa
getopts結果=?

引数がない時と同じになる。

オプション引数を扱う。

オプション付き引数を入れる。つまり、オプションで引数を入れてみる。

my_func -a my_value

このように、設定値付きのオプションを使う、オプション引数を使う。

“a:” 文字+”:”でオプション引数

オプションに引数をつけるには、getopts で「:」を追加する

引数が一つのとき
getopts "a:" OPT
引数が二つのとき
getopts "a:b:c:" OPT
引数が三つのとき
getopts "a:b:c:" OPT
オプション付き引数を処理しない場合

オプション付き引数を処理しない場合は連続して文字を続けるだけ

getopts "abcdefg" OPT

オプション引数のサンプル

シェルスクリプトは次のように記述した $OPTARG にオプション引数が入る点が違う。

function my_func(){

  echo 引数=$@
  getopts ":a:" opts
  echo getopts結果=$opts
  echo $OPTARG

}
my_func -a aaaaaaaaa

実行結果は次の通り

引数=-a aaaaaaaaa
getopts結果=a
aaaaaaaaa

ポイントは、$OPTARG にオプションの引数が入る

複数オプションを使う

ここまでで、基本的な使い方がわかったので、実際にありそうなオプションを取り扱ってみたいと思う。

そのままでは複数オプションを扱えないので、複数回 getopts を使う。

複数回の getopts 呼び出し

先ほど、複数個のオプションを処理できないと書きましたが、getoptsを複数回呼び出せば、複数個のオプションを処理できます。

function my_func(){

  echo args=$@
  getopts ":a:b:" OPT
  echo $OPT
  echo $OPTARG

  getopts ":a:b:" OPT
  echo $OPT
  echo $OPTARG

}
my_func -a aaaaaaaaa -b bbbbbbbbbbbbb

このようにgetoptsを何回も処理したら

実行結果が次のようになる。

args=-a aaaaaaaaa -b bbbbbbbbbbbbb
a
aaaaaaaaa
b
bbbbbbbbbbbbb

ここでのポイントは。複数回getoptsを適用すれば、複数個のオプションに対応できる。という点

複数オプションをループで処理する

ここまできて、漸くネットで見かけるサンプルに近くなる。

function my_func(){

  echo args=$@

  while getopts ":a:b:" OPT ; do

    echo $OPT
    echo $OPTARG

  done

}
my_func -a aaaaaaaaa -b bbbbbbbbbbbbb

複数オプションを取得するには、なくなるまでwhileループを回すのが基本

実行結果
args=-a aaaaaaaaa -b bbbbbbbbbbbbb
a
aaaaaaaaa
b
bbbbbbbbbbbbb

while で処理するときは caseとともに使う。

function my_func(){

  echo args=$@

  while getopts ":a:b:" OPT ; do
    case $OPT
      a)  echo $OPT
           echo $OPTARG
           ;;
      b)  echo $OPT
           echo $OPTARG
           ;;
     esac
  done

}
my_func -a aaaaaaaaa -b bbbbbbbbbbbbb

これで、getopts を使える。

getopts と while + case を組み合わせて戦うことで、シェルスクリプトでもオプションと引数つきオプションを扱えることがわかった。

ただし、whileループで処理した場合引数なしには対応できない

while ループを記述したコマンドを オプションなしで起動すると・・・

my_func aaa
args=aaa
(getopts のwhile ループでは処理されない

引数が指定されてない時に注意が必要

ナニも引数が指定されない場合は、getoptsはシゴトをするが、exit(1) するのでwhile と相性が悪い

while に入れた時には、結果は空っぽになる。

“?” はどこいった?

ただし、"?“ はきちんと処理されている。 while が処理終了になるので、while 中の case に処理がこないだけ。

function my_func(){

  echo args=$@

  getopts ":a:b:" OPT
  echo $?
  echo opt=$OPT
}
my_func -a hoge
my_func
結果
args=-a hoge
0
opt=a
args=
1
opt=?

OPTには ? が入るが、コマンドの実行結果ステータスが 1 になるので whileから抜けてしまう。

なので、while getopt でオプションが全く指定されない場合の ?をwhile中で検出することが出来ない。

ここまでのまとめ

ここまでの実験でわかったことをまとめた。

  • getoptsはオプション・引数付きオプションを処理することができる。
  • getoptsは複数回起動すると複数のオプションを処理できる
  • getoptsは while と case 句を組み合わせて処理をする。
function my_func(){

  echo args=$@

  while getopts ":a:b:" OPT ; do

    case $OPT in
      a)
        echo -a の引数 $OPTARG
        ;;
      b)
        echo -b の引数 $OPTARG
        ;;
      : )
        echo "ここ"
        ;;
      \? )
        echo nothing matched
        ;;
    esac

  done

}
my_func  -a aaa
my_func
my_func -c ccc

while とともに使った場合

検出ができるのは、「不正なオプション」を渡した時に限られる、「オプションが全く未指定」は検出ができない。別途考えないとダメなようだ

注意: getoptsをfunction内で利用す場合

getopts 複数回よびだせる、そのときにどこまで処理したかを保存する。

どこまで処理したかは、変数 $OPTIND(オプションインデックス)に保存する。

そのため、getopts は OPTINDを使うので、function で使うときは、OPTIND をグローバル汚染させない注意が必要

function path() {

 local OPTIND OPTARG OPT ## ここ重要

  while getopts 'ha:d:' OPT ; do
    case $OPT in
      d)
        echo "-D entered"
        ;;
      a)
        echo "-A entered"
        ;;
      \?)
        ;;
    esac
  done
}

local OPTIND を書かないと、グローバル変数に値が入っちゃうので、getopts が含まれる関数を何度も呼び出せない。

local 宣言があるとき

function 内に local 宣言をちゃんと書いた場合・・・

function path() {

 local OPTIND OPTARG OPT

  while getopts 'ha:d:' OPT ; do
    case $OPT in
      a)
        echo "-A entered"
        ;;
    esac
  done
  echo end
}
### ここで関数の実行
path -a aaa
path -a aaa
path -a aaa

実行と結果

-A entered
end
-A entered
end
-A entered
end

local 宣言がないとき。

function path() {
# local 宣言をコメントアウト
#local OPTIND OPTARG OPT 

  while getopts 'ha:d:' OPT ; do
    case $OPT in
      a)
        echo "-A entered"
        ;;
    esac
  done
  echo end
}
path -a aaa
path -a aaa
path -a aaa

実行結果

-A entered
end
end
end

このようにlocal宣言がないと変数$OPTIND が作用して、オプションを取れなくなる。

1つのファイルでシェルスクリプトを記述するのなら問題ないのだけれど、関数を作って連続して使うなら変数を共有しない工夫が必要

getoptsをファイルで使う場合

getoptsをファイルで使う場合は、環境変数をexport しない限りファイル内部で完結するので、管理は難しくない。

my_script.sh
#!/usr/bin/env bash
  while getopts 'ha:d:' OPT ; do
    case $OPT in
      a)
        echo "-A entered"
        ;;
    esac
  done
  echo end

ファイルに記述した場合

my_script.sh -a aaaa
my_script.sh -a aaaa
my_script.sh -a aaaa

としてもOPTINDはファイルごとに確保されるため全然影響がなく、問題ない。

getopts まとめ

  • getopts はシェル組み込み関数(builtinコマンド)
  • getopts “abc” とかけば -a -b -c とオプションを作れる
  • 複数個のオプションを一度に処理するには getoptsを複数回呼び出し
  • 複数個のオプションを処理するには while + case を使う
  • オプション引数を使うには getopts “a:b:c"とする
  • エラー処理を自分でやるには getopts “:a” と先頭に「 : 」をつける
  • 関数内で getopts を使うときは local OPT をつける

参考資料

http://stackoverflow.com/questions/16654607/using-getopts-inside-a-bash-function