getopts 入門 - シェルスクリプトでオプション引数を処理する
この記事で解決できること
- シェルスクリプトで
-a-b valueのような オプション引数を安全に処理 できる getoptsのoptstring/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 $2 を case 文で泥臭く分解する代わりに、標準化された方法でオプションを処理できる。
基本構文は次のとおり。
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.txtのresult.txtOPTIND: 次に処理する引数の位置(番号)。最初は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. ロングオプションを渡してしまう
--verbose を getopts に渡すと、- の後の各文字(-, v, e...)を個別オプションとして解釈し、意図しない動作になる。ロングオプションが必要なら設計段階で getopt を選ぶ。
まとめ
getopts はシェルスクリプトのオプション解析を 標準化された移植性の高い方法 で実装できる組み込みコマンド。while getopts ":a:bc" opt; do case ... done の型を覚え、silent モード(先頭 :)でエラーを自前ハンドリングし、最後に shift $((OPTIND - 1)) で位置引数を取り出す——この 3 点を押さえれば実務で十分戦える。