getopts 入門 - シェルスクリプトでオプション引数を処理する

getopts 入門 - シェルスクリプトでオプション引数を処理する

この記事で解決できること

  • シェルスクリプトで -a -b value のような オプション引数を安全に処理 できる
  • getoptsoptstring / OPTARG / OPTIND役割と書き方 が分かる
  • 不正オプション・引数不足を 自前でハンドリング できる(silent モード)
  • getopts(シェル組み込み)と getopt(外部コマンド)の 使い分け が分かる

結論(実務の型)

  • オプション解析は 自前パースより getopts(POSIX 準拠・移植性が高い)
  • ループは while getopts ":a:bc" opt; do ... done の形が基本
  • 引数を取るオプションは optstring で 文字: と書く(a:
  • 解析後は shift $((OPTIND - 1))残りの位置引数 を取り出す

前提(対象環境)

  • bash / POSIX sh(getopts は組み込みコマンド)
  • 扱えるのは 短いオプション-a -v)のみ。--verbose のようなロングオプションは非対応

getopts とは何か?

結論: getopts はシェル組み込みのオプション解析コマンド。while ループと組み合わせて -a -b value 形式の引数を 1 つずつ取り出す。

getoptsコマンドラインのオプション(- で始まる短い引数)を 1 つずつ解析する ためのシェル組み込みコマンドである。$1 $2case 文で泥臭く分解する代わりに、標準化された方法でオプションを処理できる。

基本構文は次のとおり。

getopts optstring name [args]
  • optstring: 受け付けるオプション文字の一覧(例: "abc" なら -a -b -c
  • name: 検出したオプション文字を格納する変数名
  • 呼び出すたびに 次のオプションを 1 つ 処理し、オプションが残っていれば終了ステータス 0(真)を返す

この「残っている間は真を返す」性質を使い、while でループするのが定石である。

なぜ自前パースより getopts を使うのか?

結論: getopts は結合オプション(-abc)や -- の扱いを標準仕様どおり処理する。case 文の自前実装はこれらを取りこぼしやすい。

自前で while [ $# -gt 0 ]case "$1" in を書くと、次のような細かい仕様を すべて自分で実装 する羽目になる。

  • 結合オプション: -a -b -c-abc とまとめて書けるようにする
  • 引数付きオプション: -b value-bvalue の両方を受け付ける
  • 終端記号 --: それ以降をオプションとして扱わない
  • 不正オプション・引数不足のエラー検出

getopts はこれらを POSIX 標準の挙動 で処理する。移植性が高く、bash 専用の構文に依存しないため、#!/bin/sh のスクリプトでもそのまま動く。

ロングオプション(--output file)が必須でなければ、まず getopts を検討する。コードが短くなり、挙動も標準化される。

getopts の基本的な使い方は?

結論: optstring で受け付ける文字を宣言し、引数を取る文字には : を付ける。OPTARG に引数値、OPTIND に次の位置が入る。

optstring の書き方

optstring は受け付けるオプション文字を並べた文字列。引数を取るオプションは文字の後ろに : を付ける

optstring 意味
"abc" -a -b -c(いずれも引数なし)
"a:bc" -a は引数あり、-b -c は引数なし
":a:bc" 先頭 : で silent モード(後述)

基本テンプレート

#!/bin/bash

verbose=0
output=""

while getopts "vo:" opt; do
  case "$opt" in
    v) verbose=1 ;;
    o) output="$OPTARG" ;;
    \?) echo "不明なオプション: -$OPTARG" >&2; exit 1 ;;
  esac
done

shift $((OPTIND - 1))

echo "verbose=$verbose output=$output"
echo "残りの引数: $*"

実行例:

$ ./script.sh -v -o result.txt input1 input2
verbose=1 output=result.txt
残りの引数: input1 input2

OPTARG と OPTIND

getopts は解析の過程で 2 つの変数を自動更新する。

  • OPTARG: 引数を取るオプション(o: 等)の 引数値 が入る。上の例では -o result.txtresult.txt
  • OPTIND: 次に処理する引数の位置(番号)。最初は 1。解析が終わると「最初の非オプション引数」を指す

ループ後に shift $((OPTIND - 1)) を実行すると、処理済みのオプションがすべて取り除かれ、$1 以降に オプション以外の引数(ファイル名等)だけ が残る。

OPTIND はシェル起動時に 1 で初期化される。同じシェル内で getopts ループを 2 回 回す関数等では、ループの前に手動で OPTIND=1 にリセットしないと 2 回目が正しく動かない。

エラー処理はどうするのか?

結論: optstring の先頭に : を置くと silent モードになり、不正オプションは ?、引数不足は : として name に入り、OPTARG に該当オプション文字が入る。

getopts のエラー報告には 2 つのモードがある。

通常モード(optstring が : で始まらない)

不正オプションや引数不足を検出すると、getopts標準エラーへ自動でメッセージを出力 し、name? を格納する。手軽だが、メッセージの文面を制御できない。

silent モード(optstring の先頭に :

optstring を ":vo:" のように 先頭 : で始めると、getopts は自動メッセージを出さず、すべてのエラーをスクリプト側で処理できる。挙動は次のとおり。

状況 name の値 OPTARG の値
不正なオプション ? 入力された不正な文字
引数が不足 : 引数を必要としたオプション文字

silent モードでのエラーハンドリング例:

while getopts ":vo:" opt; do
  case "$opt" in
    v) verbose=1 ;;
    o) output="$OPTARG" ;;
    \?) echo "不明なオプション: -$OPTARG" >&2; exit 1 ;;
    :)  echo "オプション -$OPTARG には引数が必要です" >&2; exit 1 ;;
  esac
done

実務では silent モードを推奨。エラーメッセージを日本語化したり、usage() を呼んで終了したりと、ユーザー向けの出力を完全にコントロールできる。

getopts と getopt の違いは?

結論: getopts はシェル組み込みで短いオプション専用。ロングオプション(--verbose)が必要なら外部コマンド getopt(util-linux)を使う。

名前が 1 文字違うだけの紛らわしい 2 つだが、別物である。

項目 getopts(組み込み) getopt(外部コマンド)
実体 シェル組み込みコマンド /usr/bin/getopt(util-linux 等)
ロングオプション 非対応(-a のみ) 対応(--all
移植性 POSIX 標準で高い 実装差あり(GNU 拡張版が必要なことも)
使い方 while ループで都度取得 一括で並べ替えてから解析

短いオプションだけで足りるなら getopts--output のようなロングオプションが要件なら getopt(GNU 拡張版)を検討する。

ロングオプションを getopts だけで扱う裏技(- を引数ありオプションとして処理し OPTARG を再解析する手法)も存在するが、可読性が下がる。要件が固いなら素直に getopt を使うほうが保守しやすい。

よくあるハマりどころは?

結論: OPTIND のリセット忘れ、shift の付け忘れ、ロングオプション非対応の 3 つが定番。

1. 関数内で OPTIND をリセットしない

関数内で getopts を使うと、OPTIND が前回の値を引きずる。ループ前に local OPTIND または OPTIND=1 を入れる。

parse_args() {
  local OPTIND   # 関数ローカルにして毎回リセット
  while getopts ":vo:" opt; do
    # ...
  done
}

2. shift を忘れて位置引数がずれる

shift $((OPTIND - 1)) を忘れると、$1 がオプション文字列のままになり、後続のファイル処理が壊れる。ループ直後に必ず実行 する。

3. ロングオプションを渡してしまう

--verbosegetopts に渡すと、- の後の各文字(-, v, e...)を個別オプションとして解釈し、意図しない動作になる。ロングオプションが必要なら設計段階で getopt を選ぶ。

まとめ

getopts はシェルスクリプトのオプション解析を 標準化された移植性の高い方法 で実装できる組み込みコマンド。while getopts ":a:bc" opt; do case ... done の型を覚え、silent モード(先頭 :)でエラーを自前ハンドリングし、最後に shift $((OPTIND - 1)) で位置引数を取り出す——この 3 点を押さえれば実務で十分戦える。

次に読む