はじめに
Bashスクリプトでコマンドラインオプションを解析する際、getoptsやgetoptが定番の選択肢です。しかし、これらを使った場合でも結局は一つ一つのオプションを手動で処理する必要があり、さらに以下のような問題に直面することがあります:
-
getoptsではロングオプション(--verbose)が使えない -
getoptsではオプションと引数の順序が固定されている -
getoptを使えばオプションと引数の混在は可能だが、結局自分で解析が必要 - 配列やハッシュ型の引数が扱えない
- 同じようなcase文やif文を何度も繰り返し書く必要がある
- オプションの追加・削除時に複数箇所を修正する必要がある
- バリデーション処理を自分で実装しなければならない
- ヘルプメッセージを手動で作成し、内容を正しく保つのに手間がかかる
- オプション定義とヘルプメッセージが分離しているため、不整合が起きやすい
他の言語では高度なオプション解析ライブラリが充実しているのに、Bash専用の定番ソリューションが不足していました。この問題を解決するのがgetoptlong.shです。
getoptlong.shはPerlのGetopt::Longからインスパイアされています。Getopt::Longは、GNU getoptのロングオプション記法をPerlで実装したライブラリで、宣言的なオプション定義や豊富なデータ型などの機能を提供しています。そのアイデアは他の言語にも広がり、PythonのargparseやRubyのOptionParserなど、現代的なオプション解析ライブラリの基礎となっています。getoptlong.shは、このアイデアをBashで実現しようとするものです。
AI音声解説
README(julesに書いてもらいました)をNotebookLMに読み込ませて、getoptlong.shについて音声で紹介してもらいました。NotebookLMの音声生成は噂には聞いていましたが、実際に試してみると想像以上の出来栄えで驚きました。AIとは思えないほど自然な会話形式で、なかなか面白い仕上がりになっています。
⚠️ 注意: AI生成コンテンツのため、内容に勘違いや間違った表現が含まれています。正確な情報については、この記事の内容をご参照ください。
🎧 getoptlong.sh AI音声解説 (YouTube)
実例で見るgetoptlong.shの機能
まず、実際のスクリプトを見てgetoptlong.shでどのようなことができるかを確認してください。以下は、コマンドを指定回数繰り返し実行するrepeat.shスクリプトです。このスクリプトはコマンドの反復実行に加えて、実行間隔の調整(-i)、各サイクル後の改行挿入(-p)、実行前後のメッセージ表示(-m)などの機能も提供します:
#!/usr/bin/env bash
set -eu
declare -A OPTS=(
[ count | c :=i # repeat count ]=1
[ sleep | i @=f # interval time ]=
[ paragraph | p ? # print newline after cycle ]=
[ trace | x ! # trace execution ]=
[ debug | d # debug level ]=0
[ message | m %=(^(BEGIN|END)=) # print message at BEGIN|END ]=
)
# コールバック関数
trace() { [[ $2 ]] && set -x || set +x ; }
. getoptlong.sh OPTS "$@"
# メッセージ表示関数
message() { [[ -v message[$1] ]] && echo "${message[$1]}" || : ; }
# 実行開始
message BEGIN
for (( i = 0; $# > 0 && i < count ; i++ )) ; do
"$@"
[[ -v paragraph ]] && echo "$paragraph"
if (( ${#sleep[@]} > 0 )) ; then
time="${sleep[$(( i % ${#sleep[@]} ))]}"
sleep $time
fi
done
message END
このスクリプトの使用例:
# 基本的な使用(dateコマンドを3回実行)
$ ./repeat.sh --count 3 date
# スリープ間隔を指定して実行
$ ./repeat.sh -c 5 --sleep 1.5 echo "Hello"
# 複数のスリープ間隔を指定(順番に使用される)
$ ./repeat.sh -i .3,.3,.6 -c 9 echo "Test"
# 各サイクル後に改行を追加
$ ./repeat.sh -c 3 --paragraph echo "Line"
# 開始・終了メッセージを指定
$ ./repeat.sh -m BEGIN=Hello,END=Bye -c 2 date
# トレース実行(set -xが有効になる)
$ ./repeat.sh --trace -c 2 echo "Traced"
このスクリプトに-hオプションを指定すると、次のようなヘルプが表示されます:
$ ./repeat.sh --help
repeat.sh [ options ] args
--count=# -c# repeat count
--debug -d debug level
--help -h show HELP
--message=#=# -m#=# print message at BEGIN|END
--paragraph[=#] -p print newline after cycle
--sleep=#[,#] -i#[,#] interval time
--trace -x trace execution
このコードで実現されている機能:
-
整数バリデーション (
countの:=i) -
浮動小数点配列 (
sleepの@=f) -
省略可能な引数 (
paragraphの?) -
コールバック関数 (
traceの!) -
ハッシュオプション (
messageの%) -
正規表現バリデーション (
messageの=(^(BEGIN|END)=)) - 自動ヘルプ生成
- カンマ区切り値サポート
getoptlong.shの特徴
getoptlong.shは以下の機能を提供します:
-
GNU形式のロングオプションサポート(
--help,--verboseなど) -
ショートオプションの連結(
-abcを-a -b -cとして解釈) -
否定形式(
--no-verboseでフラグオプションを無効化) - 柔軟なオプション順序(PERMUTEモード)
- 豊富なデータ型(フラグ、必須引数、省略可能な引数、配列、ハッシュ)
- バリデーション機能(整数、浮動小数点、正規表現)
- コールバック関数による柔軟な処理
- パススルー機能(オプションと値の配列収集)
- 自動ヘルプメッセージ生成
- 複数回呼び出しによるサブコマンド対応
基本的な使い方
1. ワンライナー形式(推奨)
最もシンプルな使い方から始めましょう。このプログラムは、フラグ(--verbose)、ファイル指定(--file)、デフォルト値付きカウント(--count)の3つのオプションを持ちます:
#!/usr/bin/env bash
declare -A OPTS=(
[ jedi | j ]=
[ config | c: ]=
[ droids | d: ]=42
)
# PATHにgetoptlong.shがあれば、パスを指定する必要はありません
. getoptlong.sh OPTS "$@"
echo "jedi: $jedi"
echo "config: $config"
echo "droids: $droids"
echo "remaining args: $@"
これだけで以下のようなオプション解析が可能になります:
$ ./myscript.sh --jedi -c death-star.conf --droids 2 luke leia
jedi: 1
config: death-star.conf
droids: 2
remaining args: luke leia
説明文を指定しなくても、実用的なヘルプメッセージが自動生成されます。オプション定義から自動的に使用方法、各オプションの説明、デフォルト値まで表示してくれます:
$ ./myscript.sh -h
myscript.sh [ options ] args
--config=# -c# set CONFIG
--droids=# -d# set DROIDS (default:42)
--help -h show HELP
--jedi -j enable JEDI
2. 段階的な方法
より細かい制御が必要な場合は、段階的に処理できます:
#!/usr/bin/env bash
declare -A OPTS=(
[ output | o: ]=output.txt
[ format | f: ]=json
[ verbose | v ]=
)
# ライブラリの読み込み
. getoptlong.sh
# 初期化
getoptlong init OPTS
# 解析実行
getoptlong parse "$@"
# 変数の設定
eval "$(getoptlong set)"
# 使用
echo "Output: $output"
echo "Format: $format"
echo "Verbose: $verbose"
オプション定義の詳細
オプション定義の構文
[OPTION_NAME|ALIAS TYPE MODIFIER DESTINATION VALIDATION # DESCRIPTION]=INITIAL_VALUE
各部分の説明:
-
OPTION_NAME: ロングオプション名(
--option-name) -
ALIAS: 短いオプション名(
-o) -
TYPE: データ型(
+,:,?,@,%) -
MODIFIER: 特殊な動作(
!,>) - DESTINATION: 出力先変数名(省略時はOPTION_NAMEを使用)
-
VALIDATION: バリデーション(
=i,=f,=(正規表現)) - DESCRIPTION: ヘルプメッセージ用の説明
- INITIAL_VALUE: 初期値
データ型の種類
フラグオプション(+または省略)
declare -A OPTS=(
[ verbose | v ]= # フラグ
[ debug | d ]=0 # カウンター(初期値0)
)
# 使用例
$ ./script.sh -v --debug --debug
# verbose=1, debug=2
必須引数(:)
declare -A OPTS=(
[ file | f: ]=
[ output | o: ]=result.txt
)
# 使用例
$ ./script.sh --file input.txt
省略可能な引数(?)
declare -A OPTS=(
[ compress | c? ]= # 引数は省略可能
)
# 使用例
$ ./script.sh --compress # compress=""
$ ./script.sh --compress=9 # compress="9"
配列オプション(@)
declare -A OPTS=(
[ include | i@ ]=
)
# 使用例
$ ./script.sh -i "*.txt" --include "*.md"
# include=("*.txt" "*.md")
# カンマ区切りで複数の値を一度に指定することも可能
$ ./script.sh -i "*.txt,*.md,*.sh"
# include=("*.txt" "*.md" "*.sh")
ハッシュオプション(%)
declare -A OPTS=(
[ define | D% ]=
)
# 使用例
$ ./script.sh --define key1=value1 -D key2=value2
# define[key1]="value1", define[key2]="value2"
# カンマ区切りで複数のkey=valueペアを一度に指定することも可能
$ ./script.sh -D "key1=value1,key2=value2,key3=value3"
# define[key1]="value1", define[key2]="value2", define[key3]="value3"
値の自動分割
配列オプション(@)やハッシュオプション(%)では、カンマ区切りで複数の値を一度に設定できます。これはDELIM設定によるもので、デフォルトでスペース、タブ、カンマが区切り文字として認識されます。
修飾子(MODIFIER)
コールバック修飾子(!)
オプションが指定されたときに自動的に関数を呼び出します:
#!/usr/bin/env bash
# コールバック関数を定義
trace() {
[[ $2 ]] && set -x || set +x
}
declare -A OPTS=(
[ trace | x ! # trace execution ]=
[ verbose | v ]=
)
. getoptlong.sh OPTS "$@"
# --trace が指定されると trace 関数が自動実行される
echo "This will be traced if --trace was specified"
関数には第1引数($1)にオプション名、第2引数($2)に値が渡されます。
パススルー修飾子(>)
オプションとその値をそのまま配列に収集します:
declare -A OPTS=(
[ docker-opt | d :>docker_args ]= # --docker-opt の値をdocker_args配列に収集
[ verbose | v ]=
)
. getoptlong.sh OPTS "$@"
# 収集されたオプションを他のコマンドに渡す
docker run "${docker_args[@]}" ubuntu
複数のオプションを同じ配列にまとめることも可能です:
declare -A OPTS=(
[ input | i :>files ]=
[ output | o :>files ]=
[ config | c :>files ]=
)
. getoptlong.sh OPTS "$@"
# すべてのファイル関連オプションがfiles配列に収集される
echo "All file options: ${files[@]}"
DESTINATION変数の指定
デフォルトの変数名
デフォルトでは、オプション名がそのまま変数名になります:
declare -A OPTS=(
[ verbose | v ]=
[ count | c: ]=
)
# verbose=1, count=5 のような変数が作成される
明示的な変数名指定
DESTINATION部分で出力先変数名を明示的に指定できます:
declare -A OPTS=(
[ count | c :COUNT=i ]=1 # countオプションの値をCOUNT変数に格納
[ debug | d +DEBUG ]=0 # debugオプションの値をDEBUG変数に格納
[ files | f @FILES ]= # filesオプションの値をFILES配列に格納
[ config | c %CONFIG ]= # configオプションの値をCONFIG連想配列に格納
)
. getoptlong.sh OPTS "$@"
echo "Count: $COUNT"
echo "Debug level: $DEBUG"
echo "Files: ${FILES[@]}"
echo "Config keys: ${!CONFIG[@]}"
バリデーション
getoptlong.shでは、オプションの値に対して自動的なバリデーションを行うことができます。
バリデーション種類
整数バリデーション(=i)
declare -A OPTS=(
[ port | p :=i ]=8080
)
# 使用例
$ ./script.sh --port 1337 # OK
$ ./script.sh --port abc # エラー: abc: not an integer
浮動小数点バリデーション(=f)
declare -A OPTS=(
[ rate | r :=f ]=1.5
)
# 使用例
$ ./script.sh --rate 2.718 # OK
$ ./script.sh --rate .5 # OK
$ ./script.sh --rate hello # エラー: hello: not a float
正規表現バリデーション(=(pattern))
declare -A OPTS=(
[ zipcode | z :=(^[0-9]{3}-[0-9]{4}$) ]= # 郵便番号
[ color | c :=(^(red|green|blue)$) ]= # 選択肢
)
# 使用例
$ ./script.sh --zipcode 101-0054 # OK
$ ./script.sh --zipcode 12345 # エラー: 12345: does not match pattern
$ ./script.sh --color red # OK
$ ./script.sh --color yellow # エラー: yellow: does not match pattern
正規表現バリデーションでは、パターンを括弧で囲みます。バリデーションに失敗した場合、スクリプトはエラーメッセージを出力して終了します。
ヘルプメッセージの自動生成
getoptlong.shは自動的にヘルプオプション(--help, -h)を追加し、オプション定義からヘルプメッセージを生成します:
declare -A OPTS=(
[ verbose | v # 詳細な出力を有効にする ]=
[ file | f: # 入力ファイルのパス ]=
[ count | c: # 繰り返し回数 ]=5
)
. getoptlong.sh OPTS "$@"
$ ./script.sh --help
script.sh [ options ] args
--count=# -c# 繰り返し回数
--file=# -f# 入力ファイルのパス
--help -h show HELP
--verbose -v 詳細な出力を有効にする
注意:ヘルプメッセージのオプションはアルファベット順にソートされて表示されます。
他のオプション解析ライブラリとの比較
シェルスクリプト用のオプション解析ライブラリには複数の選択肢があります。なかでも注目すべきは getoptions で、これは POSIX 互換で複数シェル対応の軽量ライブラリです。外部コマンドに依存しない純粋なシェルスクリプト実装により、dash、bash、ksh、zshなど幅広いシェルで動作します。
主要なライブラリと getoptlong.sh を比較してみましょう。
機能比較表
| 機能 | getopts | getoptions | getoptlong.sh |
|---|---|---|---|
| 対応シェル | POSIX | POSIX | Bash専用 |
| ロングオプション | ❌ | ✅ | ✅ |
| ショートオプション連結 | ✅ | ✅ | ✅ |
| 否定形式 | ❌ | ✅ | ✅ |
| +で始まるオプション | ❌ | ✅ | ❌ |
| オプション名省略 | ❌ | ✅ | ❌ |
| サブコマンド | ❌ | ✅ | ✅ |
| オプション順序 | 固定 | 柔軟 | 柔軟(PERMUTE) |
| データ型 | 文字列のみ | 文字列のみ | 豊富(配列,ハッシュ等) |
| バリデーション | ❌ | 限定的 | ✅(正規表現対応) |
| ヘルプ生成 | 手動 | 自動 | 自動 |
| コールバック | ❌ | ✅ | ✅ |
| パススルー機能 | ❌ | ❌ | ✅ |
| 設定方法 | 手続き的 | 宣言的 | 宣言的 |
| 外部依存 | なし | なし | なし |
| 学習コスト | 低 | 中 | 中 |
オプション定義の比較
同じ機能を実現する場合の定義例を比較してみましょう:
getoptions
parser_definition() {
setup REST help:usage -- "Example script"
flag FLAG -f --flag
param FILE -F --file
option OPTION -o --option init:="default"
}
eval "$(getoptions parser_definition) exit 1"
getoptlong.sh
declare -A OPTS=(
[ flag | f ]=
[ file | F: ]=
[ option | o? ]=default
)
. getoptlong.sh OPTS "$@"
getoptlong.sh の方がより簡潔で、連想配列による直感的な定義が特徴です。
各ライブラリの特徴と選択指針
getoptlong.sh の強み
- Bashの機能をフル活用: 連想配列、配列、ハッシュをネイティブサポート
- 豊富なデータ型: 文字列、配列、ハッシュ、正規表現バリデーション、コールバック関数
- 簡潔な記述: ワンライナーでフル機能のオプション解析が可能
- 高度な機能: パススルー、変数名指定、カンマ区切り値など
# getoptlong.sh の例 - 簡潔で高機能
declare -A OPTS=(
[ config | c %=(^[A-Z_]+=.+$) ]= # ハッシュ + 正規表現バリデーション
[ files | f @=f ]= # 配列 + 浮動小数点バリデーション
[ trace | t ! ]= # コールバック関数の自動実行
)
. getoptlong.sh OPTS "$@"
getoptions の強み
- POSIX互換: 幅広いシェル環境で動作する高いポータビリティ
- 純粋シェルスクリプト: 外部コマンドに依存しない軽量実装
- 高パフォーマンス: 高速な解析処理
- 豊富なオプション: +オプション、オプション名省略機能
比較ポイント
| 観点 | getoptions | getoptlong.sh |
|---|---|---|
| ポータビリティ | POSIX 互換 | Bash 専用 |
| 記述量 | やや多い | 簡潔 |
| 高度な機能 | 基本的 | 豊富(配列、ハッシュ、正規表現) |
| Bash連携 | 限定的 | ネイティブ |
| パフォーマンス | 高速 | 中程度 |
| 実装方式 | 純粋シェルスクリプト | Bash特化 |
選択の指針
getoptlong.sh が適している場合:
- Bash環境が確定している
- 豊富なデータ型が必要(配列、ハッシュ)
- 複雑なバリデーションが必要
- 開発効率を重視
- Bashの機能を活用したい
他のライブラリが適している場合:
- 複数のシェル環境で動作させたい
- 軽量性・高速性を重視
- ポータビリティが重要
- 既存のPOSIXスクリプトに組み込みたい
まとめ
getoptlong.shを使うことで、Bashスクリプトでも本格的なコマンドラインツールと同等のオプション解析が可能になります。特に以下の点で従来のgetoptsから大幅に改善されます:
- 開発効率の向上: ワンライナーで高機能なオプション解析
- ユーザビリティの向上: ロングオプション、自動ヘルプ、柔軟な引数順序
- 保守性の向上: 型安全性、バリデーション、構造化されたオプション定義
Bash専用という制約はありますが、その分Bashの機能を最大限活用できる設計になっています。複雑なBashスクリプトを書く際は、ぜひgetoptlong.shの導入を検討してみてください。スクリプトの品質とユーザー体験が大幅に向上するはずです。
実はgetoptlong.sh内部ではgetoptsを使っています。「一生使わない」と言いつつ、結局は裏で動いているということになりますが、重要なのはあなたが直接getoptsと格闘する必要がもうないということです。どのように使っているかを見たい酔狂な人は、コードを読んでみてください。
参考リンク
- getoptlong.sh GitHub リポジトリ
- getoptions GitHub リポジトリ
- Getopt::Long (Perl) - getoptlong.shのインスピレーション元
- Getopt::EX::Hashed GitHub リポジトリ
- より詳細な例は
ex/ディレクトリをご確認ください
getoptions 関連記事
- getoptions を使って面倒なシェルスクリプトのオプション解析コードを自動生成しよう!
- シェルスクリプト(bash等)の引数解析が究極的に簡単になりました
- 簡単に使えるエレガントなオプション解析ライブラリ(シェルスクリプト用)
- シェルスクリプト オプション解析 徹底解説 (getopt / getopts)
この記事について
この記事はClaude Codeによって生成したものを無編集で掲載しています。