getoptsによるフラグと引数の解析
getoptsを使って短いオプション、必須引数、使用方法メッセージに対応した、実用的なコマンドラインインターフェースを実装します。
「getoptsによるフラグと引数の解析」はCoddyKit上の無料DevOps Bootcampレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはDevOps Bootcamp学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 DevOps Bootcampコースには全4レッスンが含まれています。
getopts が存在する理由
実用的なスクリプトでは、いずれ -v、-o output.txt、-n 5 のようなオプションを受け付ける必要が生じます。これらを $1、$2…で手作業で解析すると、すぐに壊れやすくなります。
getopts は、引数を取るオプションを含む短いオプション(-a、-b)を確実に処理する、POSIX 標準の組み込みコマンドです。すべての POSIX シェルに組み込まれているため、依存関係は必要ありません。
- 結合されたフラグを処理します:
-vn 5=-v -n 5 - 未知のオプションを適切に報告します
- 標準変数
OPTINDとOPTARGを自動的に設定します
このレッスンでは、getopts を使って完全なプロフェッショナル向け CLI をゼロから構築します。
getopts の構文
基本構文は、各反復で getopts を呼び出す while ループです。
while getopts "optstring" varname; do
case "$varname" in
...
esac
done- optstring — 受け付ける各オプション文字を並べた文字列です。文字の後ろにコロンを付けると、そのオプションが引数を必要とすることを示します。
- varname — 各反復で現在のオプション文字を受け取ります。
- OPTARG — 文字の後ろにコロンがある場合、引数の値が自動的に設定されます。
- OPTIND — 次に処理する引数のインデックスです。ループ後に
shift $((OPTIND - 1))でシフトすると、残りの位置パラメーターを取り出せます。
#!/usr/bin/env bash
# Minimal skeleton — shows the loop structure
while getopts 'vn:' opt; do
case "$opt" in
v) echo "Verbose mode on" ;;
n) echo "Count = $OPTARG" ;;
?) echo "Unknown option: -$OPTARG" >&2; exit 1 ;;
esac
doneOptstring の定義
optstring は、CLI の仕様を簡潔に宣言するものです。各文字が、受け付ける1つのフラグを表します。
'abc'—-a、-b、-cを受け付けます(引数なし)'a:bc'—-aは引数必須、-bと-cは引数不要です':abc'— 先頭のコロンによりサイレントエラーモードが有効になります(シェルではなくスクリプトが不正なオプションを処理します)
サイレントモードは、エラーメッセージと終了コードを完全に制御できるため、本番用スクリプトで推奨されます。
#!/usr/bin/env bash
# optstring ':o:vq'
# -o requires an argument (output file)
# -v verbose flag (no argument)
# -q quiet flag (no argument)
# Leading ':' = silent error mode
while getopts ':o:vq' opt; do
case "$opt" in
o) OUTPUT="$OPTARG" ;;
v) VERBOSE=1 ;;
q) QUIET=1 ;;
:) echo "Error: -$OPTARG requires an argument" >&2; exit 1 ;;
\?) echo "Error: unknown option -$OPTARG" >&2; exit 1 ;;
esac
done
echo "OUTPUT=$OUTPUT VERBOSE=$VERBOSE QUIET=$QUIET"OPTARG と必須引数
optstring でフラグの後ろにコロンを付けると、getopts はその値を OPTARG に格納します。引数はスペースを付けても付けなくても指定できます。
-o report.txt-oreport.txt
どちらの形式も同じように解析されます。これは、手動で $1/shift を使って解析する場合に対する大きな利点の1つです。
サイレントモード(optstring の先頭が ':')では、引数がないと getopts は varname に : を、OPTARG にオプション文字を設定します。対象を絞ったエラーメッセージを表示するのに最適です。
#!/usr/bin/env bash
# Demonstrate OPTARG with a file-processing script
while getopts ':i:o:' opt; do
case "$opt" in
i) INPUT="$OPTARG" ;;
o) OUTPUT="$OPTARG" ;;
:) echo "Error: option -$OPTARG needs a value" >&2; exit 1 ;;
\?) echo "Error: unknown flag -$OPTARG" >&2; exit 1 ;;
esac
done
echo "Input : ${INPUT:-<not set>}"
echo "Output : ${OUTPUT:-<not set>}"OPTIND でオプションをシフトする
getopts の処理が終わると、OPTIND には最初の非オプション引数のインデックスが格納されています。shift を使って処理済みのオプションをすべて取り除くと、$1、$2…が残りの位置パラメーター(ファイル名など)を参照するようになります。
定型句は常に次のとおりです。
shift $((OPTIND - 1))シフト後、$@ にはフラグではない引数、つまりコマンドのオペランドだけが含まれます。
#!/usr/bin/env bash
# Shows OPTIND shift and leftover positional args
VERBOSE=0
while getopts ':vn:' opt; do
case "$opt" in
v) VERBOSE=1 ;;
n) COUNT="$OPTARG" ;;
:) echo "Error: -$OPTARG requires an argument" >&2; exit 1 ;;
\?) echo "Error: unknown option -$OPTARG" >&2; exit 1 ;;
esac
done
shift $((OPTIND - 1))
echo "VERBOSE=$VERBOSE COUNT=${COUNT:-1}"
echo "Remaining args: $*"Usage 関数を書く
プロフェッショナルなスクリプトには、ヘルプメッセージを表示して終了する usage() 関数を必ず用意します。一般的な慣例は次のとおりです。
- パイプで渡す出力を汚さないよう、stderr(ファイルディスクリプター 2)に出力します
-h/--helpでは終了コード 0、不正な使い方では終了コード 1 で終了します- エラー処理では
usage 1を、-hのハンドラーではusage 0を呼び出します
#!/usr/bin/env bash
usage() {
cat >&2 <<EOF
Usage: $(basename "$0") [-v] [-n COUNT] [-o FILE] [FILE...]
Options:
-v Verbose output
-n COUNT Repeat COUNT times (default: 1)
-o FILE Write output to FILE
-h Show this help
EOF
exit "${1:-0}"
}
while getopts ':vn:o:h' opt; do
case "$opt" in
v) VERBOSE=1 ;;
n) COUNT="$OPTARG" ;;
o) OUTFILE="$OPTARG" ;;
h) usage 0 ;;
:) echo "Error: -$OPTARG requires a value" >&2; usage 1 ;;
\?) echo "Error: unknown option -$OPTARG" >&2; usage 1 ;;
esac
done
shift $((OPTIND - 1))
echo "Parsed OK — verbose=${VERBOSE:-0} count=${COUNT:-1} out=${OUTFILE:--}"デフォルト値と検証
解析後、実際の処理を始める前に入力を検証し、デフォルト値を設定します。解析フェーズ(ループ)とロジックフェーズを分離してください。これにより、どちらの部分も読みやすく、テストしやすくなります。
- インラインのデフォルト値には
${VAR:-default}を使います - 正規表現または算術チェックで数値引数を検証します
- 必須オプションが実際に指定されたかを検証します
#!/usr/bin/env bash
usage() { echo "Usage: $(basename "$0") -n COUNT [-v]" >&2; exit 1; }
VERBOSE=0
COUNT=''
while getopts ':n:v' opt; do
case "$opt" in
n) COUNT="$OPTARG" ;;
v) VERBOSE=1 ;;
:) echo "Error: -$OPTARG needs a value" >&2; usage ;;
\?) echo "Error: -$OPTARG unknown" >&2; usage ;;
esac
done
shift $((OPTIND - 1))
# Validation phase
[[ -z "$COUNT" ]] && { echo "Error: -n COUNT is required" >&2; usage; }
[[ "$COUNT" =~ ^[0-9]+$ ]] || { echo "Error: COUNT must be a positive integer" >&2; usage; }
for (( i=1; i<=COUNT; i++ )); do
[[ $VERBOSE -eq 1 ]] && echo "Iteration $i of $COUNT"
echo "Hello, world!"
doneコマンドラインでフラグを結合する
getopts は、スペースなしで記述された短いフラグの結合を自動的に処理します。これは標準的な Unix の慣例です。
-v -qは-vqと同じです-n 5 -vは-n5 -vまたは-vn5と同じです
これをサポートするために追加のコードを書く必要はありません。getopts が結合されたオプション文字列を各文字に分けて自動的に反復処理します。手動解析ではなく getopts を使うもう1つの大きな理由です。
#!/usr/bin/env bash
# Test combined flag parsing
# Run as: bash script.sh -vq -n3
VERBOSE=0; QUIET=0; COUNT=1
while getopts ':vqn:' opt; do
case "$opt" in
v) VERBOSE=1 ;;
q) QUIET=1 ;;
n) COUNT="$OPTARG" ;;
:) echo "Error: -$OPTARG needs value" >&2; exit 1 ;;
\?) echo "Error: unknown -$OPTARG" >&2; exit 1 ;;
esac
done
shift $((OPTIND - 1))
echo "verbose=$VERBOSE quiet=$QUIET count=$COUNT"ダブルダッシュ区切り文字の処理
Unix コマンドでは、オプション処理の停止を明示的に示すために --(ダブルダッシュ)を使えます。-- の後ろにあるものは、フラグのように見えてもすべて位置引数として扱われます。
getopts は -- に遭遇すると自動的に停止しますshift $((OPTIND - 1)) の後にはダブルダッシュが取り除かれ、$@ にはオペランドだけが含まれます。
これは、次の例のように、ダッシュで始まる可能性のあるファイル名を扱うスクリプトで重要です。
myscript.sh -v -- -strangefile.txt#!/usr/bin/env bash
# Demonstrate -- separator
# Run as: bash script.sh -v -- file1.txt -oddname.txt
VERBOSE=0
while getopts ':v' opt; do
case "$opt" in
v) VERBOSE=1 ;;
\?) echo "Unknown option -$OPTARG" >&2; exit 1 ;;
esac
done
shift $((OPTIND - 1)) # removes -v and the '--' separator
echo "Verbose: $VERBOSE"
echo "Files to process:"
for f in "$@"; do
echo " -> $f"
donegetopts をライブラリ関数でラップする
モジュール化されたスクリプトでは、getopts をparse_args() 関数にカプセル化し、グローバル変数(または nameref 変数)を設定できます。これにより main() がすっきりし、他のスクリプトからパーサーを source できるようになります。
このパターンの主なルールは次のとおりです。
- 関数を呼び出す前にオプション変数を宣言します
global変数を使うか、nameref(declare -n)で値を渡します- 入力が不正な場合はゼロ以外の終了コードを返し、
mainが対応できるようにします
#!/usr/bin/env bash
# Global option variables
VERBOSE=0; OUTPUT=''; COUNT=1
parse_args() {
local opt
while getopts ':vn:o:h' opt; do
case "$opt" in
v) VERBOSE=1 ;;
n) COUNT="$OPTARG" ;;
o) OUTPUT="$OPTARG" ;;
h) echo "Usage: $(basename "$0") [-v] [-n N] [-o FILE]"; exit 0 ;;
:) echo "Error: -$OPTARG needs a value" >&2; return 1 ;;
\?) echo "Error: unknown option -$OPTARG" >&2; return 1 ;;
esac
done
shift $((OPTIND - 1))
ARGS=("$@") # leftover positional args stored in array
}
main() {
parse_args "$@" || exit 1
echo "verbose=$VERBOSE count=$COUNT output=${OUTPUT:--} args=${ARGS[*]}"
}
main "$@"実践的な完全例:ログアーカイバー
このレッスンで扱った内容をすべて使う、実践的で完全なスクリプトを紹介します。必須引数付きの optstring、サイレントエラーモード、usage 関数、デフォルト値、検証、そして OPTIND のシフトを使用しています。
構造をよく確認してください。CLI インターフェースが必要なスクリプトを書くときは、すべてこのテンプレートに従うとよいでしょう。
#!/usr/bin/env bash
# archive_logs.sh — compress and move logs older than N days
set -euo pipefail
DESTDIR='/tmp/log_archive'
DAYS=30
VERBOSE=0
usage() {
cat >&2 <<EOF
Usage: $(basename "$0") [-v] [-d DAYS] [-o DIR] SOURCE_DIR
-d DAYS Archive logs older than DAYS (default: 30)
-o DIR Destination directory (default: /tmp/log_archive)
-v Verbose output
-h Show this help
EOF
exit "${1:-0}"
}
while getopts ':d:o:vh' opt; do
case "$opt" in
d) DAYS="$OPTARG" ;;
o) DESTDIR="$OPTARG" ;;
v) VERBOSE=1 ;;
h) usage 0 ;;
:) echo "Error: -$OPTARG requires a value" >&2; usage 1 ;;
\?) echo "Error: unknown option -$OPTARG" >&2; usage 1 ;;
esac
done
shift $((OPTIND - 1))
# Validation
[[ $# -lt 1 ]] && { echo "Error: SOURCE_DIR is required" >&2; usage 1; }
[[ "$DAYS" =~ ^[0-9]+$ ]] || { echo "Error: DAYS must be numeric" >&2; exit 1; }
SOURCE="$1"
[[ -d "$SOURCE" ]] || { echo "Error: '$SOURCE' is not a directory" >&2; exit 1; }
mkdir -p "$DESTDIR"
[[ $VERBOSE -eq 1 ]] && echo "Archiving logs older than $DAYS days from $SOURCE to $DESTDIR"
find "$SOURCE" -name '*.log' -mtime "+$DAYS" -print | while read -r f; do
gzip -c "$f" > "$DESTDIR/$(basename "$f").gz"
[[ $VERBOSE -eq 1 ]] && echo " archived: $f"
done
echo "Done."クイックチェック:getopts の Optstring
次の getopts 呼び出しを読み、その動作について正しい説明を選んでください。
while getopts ':f:vq' opt; doレッスンのまとめ:getopts の習得
getopts を使って Bash でプロフェッショナルなコマンドラインインターフェースを構築する方法を学びました。重要なポイントをまとめます。
- Optstring の構文 — コロンのない文字はブール型フラグ、文字の後ろにあるコロンは引数が必要であることを示します。先頭のコロンでサイレントエラーモードが有効になります。
- OPTARG — 引数が必要なオプションの引数値を自動的に保持します。
- OPTIND — ループ後に
shift $((OPTIND - 1))を使うと、$@から残りの位置パラメーターを取り出せます。 - サイレントエラーモード — 本番環境で推奨されます。
:(引数不足)と\?(未知のオプション)のケースを自分で処理することで、完全に制御できます。 - usage() 関数 — 必ず作成し、stderr に出力します。
-hでは 0、エラー時には 1 で終了します。 - 結合されたフラグ — getopts は追加のコードなしで
-vqや-n5を自動的に処理します。 - モジュール化パターン — getopts を
parse_args()関数でラップすると、すっきりした再利用可能なスクリプトになります。
getopts を習得すると、スクリプトは単一目的のツールから、Unix の慣例に従う信頼性の高い使いやすい CLI プログラムへと進化します。
よくある質問
「getoptsによるフラグと引数の解析」レッスンは無料ですか?
はい。「getoptsによるフラグと引数の解析」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、DevOps Bootcampコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 DevOps Bootcampコースには全4レッスンが含まれています。
「getoptsによるフラグと引数の解析」で何を学びますか?
getoptsを使って短いオプション、必須引数、使用方法メッセージに対応した、実用的なコマンドラインインターフェースを実装します。 ブラウザで直接実行するハンズオンコードでDevOps Bootcampを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
DevOps Bootcampを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのDevOps Bootcampは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「getoptsによるフラグと引数の解析」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このDevOps Bootcampレッスンでコードを書いて実行できますか?
はい。すべてのDevOps Bootcampレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- ローカルスコープと終了コードを使った関数設計
- 再利用可能なBashライブラリの構築と読み込み
- getoptsによるフラグと引数の解析
- 関数間での配列と連想マップの受け渡し