シェルスクリプトにロングオプションを実装する方法|getoptコマンドで--verbose・--outputを安全に処理する設計

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOME > Linux技術 リナックスマスター.JP(Linuxマスター.JP) > シェルスクリプト > シェルスクリプトにロングオプションを実装する方法|getoptコマンドで--verbose・--outputを安全に処理する設計
「オプションを増やしたら getoptsで書いたスクリプトが限界になった」「--output=file みたいなロングオプション形式にしたいが、どう書けばいいか分からない」
そんな悩みを抱えたことはありませんか。

bash組み込みの getopts は手軽ですが、-v や -o のような短形式(ショートオプション)にしか対応していません。ツールとして配布するスクリプトや、チームで使う運用スクリプトには、--verbose・--output=file のようなロングオプション形式が読みやすく、ミスも減ります。

この記事では、外部コマンドの getopt を使ってシェルスクリプトにロングオプションを実装する設計パターンを解説します。GNU/BSD の互換性の落とし穴から、必須引数・任意引数の定義、エラー処理との組み合わせ、そのまま流用できる完成形テンプレートまで、RHEL 9・Ubuntu 24.04 LTS の実機で確認した手順をベースに紹介します。

この記事のポイント

・getopts(組み込み)は短形式のみ、getopt(外部コマンド)はロング形式に対応
・GNU getoptは -o と --longopts で短形式・長形式を同時定義できる
・eval set -- "$ARGS" パターンでスペースを含む引数も安全に処理できる
・macOS のBSD getopt は非互換。Linux 専用か GNU getopt 前提で設計する


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

getopts の限界とgetoptが必要になるとき

getopts はbashに組み込まれた引数解析コマンドで、追加インストールは不要、POSIXにも準拠しており、軽量スクリプトには最適です。ただし、処理できるのは短形式オプション(-v・-o など1文字)に限られます。

対して外部コマンドの getopt(Linuxでは util-linux パッケージ所属)は、短形式と長形式の両方を同時に定義・処理できます。

比較項目 getopts(bash組み込み) getopt(外部コマンド)
短形式オプション(-v) 対応 対応
長形式オプション(--verbose) 非対応 対応
--option=value 形式 非対応 対応
スペースを含む引数 注意が必要 eval set -- で安全処理
POSIX 準拠 準拠 GNU 拡張あり(後述)
追加インストール 不要 util-linux(多くの環境で標準)
ツールとして使う運用スクリプト、複数人が実行する共有スクリプトでは --dry-run・--config=/path/to/file のような読みやすい長形式を採用するのが現場の定石です。

環境確認とGNU getoptのインストール

1. getoptのバージョン確認

まず、手元の環境に GNU getopt が入っているか確認します。

# getopt のバージョン確認 getopt --version

RHEL 9・CentOS Stream 9・Ubuntu 24.04 LTS の実機での出力例(筆者検証環境):

getopt from util-linux 2.37.4

この出力が出れば GNU getopt が使えます。何も出ない、または getopt: illegal option -- - のようなエラーになる場合は、macOS のBSD getopt が入っています(後述の互換性問題を参照)。

2. パッケージが入っていない場合のインストール

RHEL 系・Ubuntu ともに util-linux に含まれており、通常は最初から入っています。万一入っていない場合:

# RHEL / CentOS Stream / Rocky Linux sudo dnf install util-linux # Ubuntu / Debian sudo apt install util-linux

getoptの基本構文と引数の定義方法

1. getoptコマンドの構文

GNU getopt の基本形:

getopt -o <短形式定義> --long <長形式定義> -n '<スクリプト名>' -- "$@"

各パラメータの意味:
・-o:短形式オプションの定義文字列
・--long(または -l):長形式オプションのコンマ区切りリスト
・-n:エラーメッセージに出力するスクリプト名
・--:getopt 自身のオプション終端と、スクリプト引数の区切り
・"$@":スクリプトに渡ってきた全引数をそのまま渡す

2. 引数の有無の指定方法(コロンの使い方)

引数の必須・任意は文字の後ろのコロン数で表現します。
短形式 長形式 意味
v verbose 引数なし(フラグ)
o: output: 引数必須(省略不可)
c:: config:: 引数任意(省略可)
以下は、--verbose(フラグ)・--output=file(必須引数)・--config[=file](任意引数)を定義する例です:

ARGS=$(getopt \ -o vo:c:: \ --long verbose,output:,config:: \ -n "$(basename "$0")" \ -- "$@")

3. eval set -- のパターンとその理由

getopt はクォートで囲んだ形式に引数を正規化して標準出力に返します。この出力を eval set -- で再セットすることで、スペースや特殊文字を含む引数でも安全に処理できます。

# エラー終了時にメッセージを表示するため、getopt の戻り値を確認する ARGS=$(getopt -o vo:c:: --long verbose,output:,config:: -n "$(basename "$0")" -- "$@") || { echo "Usage: $0 [--verbose] [--output=FILE] [--config[=FILE]] [args...]" >&2 exit 1 } # 正規化した引数を $1 $2 ... にセット eval set -- "$ARGS"

eval set -- を使わずに $ARGS をそのままループで回すと、スペースを含む値(例:--output="my file.txt")が2語に分割されて誤動作します。必ず eval set -- を挟むのが GNU getopt 設計の鉄則です。

ループとshiftでオプションを処理する実装パターン

1. while ループ+case 文の基本形

VERBOSE=0 OUTPUT="" CONFIG="" while true; do case "$1" in -v|--verbose) VERBOSE=1 shift ;; -o|--output) OUTPUT="$2" shift 2 ;; -c|--config) # 任意引数の場合、値があれば $2 に入る case "$2" in ""|--*) # 値なし: デフォルトを使用 CONFIG="/etc/myapp/myapp.conf" shift ;; *) CONFIG="$2" shift 2 ;; esac ;; --) # オプション終端マーカー。以降は通常の引数 shift break ;; *) echo "Internal error: unknown option '$1'" >&2 exit 1 ;; esac done # $@ に残った非オプション引数 REMAINING_ARGS=("$@")

2. --option=value 形式と --option value 形式の自動対応

GNU getopt は --output=file.txt と --output file.txt の両方を同じように $1="--output"・$2="file.txt" に正規化してくれます。eval set -- 後は常に $2 から値を取り出す形で統一できます。これが GNU getopt を使う最大の利点のひとつです。

3. ヘルプ表示関数の設計

ロングオプション対応スクリプトにはヘルプ表示を必ずセットにします。

usage() { cat <

ヘルプは -h|--help を最初のオプションで評価してすぐ終了します:

-h|--help) usage exit 0 ;;

エラー処理との連携設計

1. set -euo pipefail との組み合わせ

スクリプト先頭で set -euo pipefail を使う設計では、getopt の失敗時に即終了する動作と相性がよいです。ただし、getopt のエラー判定は || {} で明示的に行うのが分かりやすくなります。
シェルスクリプトの設計品質を体系的に学びたい方は、シェルスクリプト実践講座もあわせてご覧ください。現場で通用する設計パターンをハンズオン形式で習得できます。

#!/bin/bash set -euo pipefail usage() { cat <&2 exit 1 } eval set -- "$ARGS" VERBOSE=0 OUTPUT="" while true; do case "$1" in -v|--verbose) VERBOSE=1; shift ;; -o|--output) OUTPUT="$2"; shift 2 ;; -h|--help) usage; exit 0 ;; --) shift; break ;; *) echo "Unknown option: $1" >&2; exit 1 ;; esac done # 必須引数の確認はオプション解析後に行う if [[ -z "$OUTPUT" ]]; then echo "Error: --output は必須です" >&2 usage >&2 exit 1 fi

2. 実機での動作確認例

上記スクリプトを myapp.sh として保存し、RHEL 9.4 の実機で確認した出力例です:

# 正常系: ロングオプションと値 $ bash myapp.sh --verbose --output=/tmp/result.log file1.txt file2.txt VERBOSE=1, OUTPUT=/tmp/result.log, REMAINING=file1.txt file2.txt # --output=value 形式でも同じ結果 $ bash myapp.sh -v --output=/tmp/result.log file1.txt VERBOSE=1, OUTPUT=/tmp/result.log, REMAINING=file1.txt # 不明なオプション(エラー) $ bash myapp.sh --invalid myapp.sh: unrecognized option '--invalid' Usage: myapp.sh [OPTIONS] [FILE...] -v, --verbose 詳細ログ -o, --output=FILE 出力先(必須) -h, --help ヘルプ表示 # 必須引数省略(エラー) $ bash myapp.sh --verbose Error: --output は必須です

GNU getoptとBSD getoptの互換性問題

1. macOS のgetoptsとgetoptの挙動

macOS に標準で入っている getopt は BSD 版であり、GNU の --long オプションに対応していません。getopt --version が何も出ないか、getopt: not an option: --version などのエラーになる環境は BSD getopt です。
環境 getoptの種類 ロングオプション対応
RHEL / CentOS Stream / Rocky Linux / Ubuntu GNU getopt(util-linux) 対応
macOS(デフォルト) BSD getopt 非対応
macOS(Homebrew gnu-getopt) GNU getopt 対応(PATH設定要)
Alpine Linux busybox getopt(制限あり) 要確認

2. ポータビリティを意識した設計方針

Linux サーバー専用スクリプトであれば GNU getopt 前提で問題ありません。macOS でも動かす必要がある場合は、次のような OS 判定で分岐させます:

# GNU getopt か BSD getopt かを判定する if getopt --version 2>&1 | grep -q 'util-linux'; then GETOPT_CMD="getopt" elif command -v ggetopt &>/dev/null; then # macOS + Homebrew gnu-getopt GETOPT_CMD="ggetopt" else echo "GNU getopt が見つかりません。util-linux または gnu-getopt をインストールしてください。" >&2 exit 1 fi

運用スクリプトは Linux サーバー上で動かすことがほとんどなので、多くの現場では「Linux 専用・GNU getopt 前提」と明記して割り切る設計で問題ありません。

完成形:本番で使える実践CLIテンプレート

これまでの要素をまとめた、そのまま使える完成形テンプレートです。

#!/bin/bash # 実行環境: RHEL 9 / Ubuntu 24.04 LTS / GNU getopt (util-linux) set -euo pipefail # ---------- ヘルプ ---------- usage() { cat <&2; exit 1; } eval set -- "$ARGS" # ---------- デフォルト値 ---------- VERBOSE=0 OUTPUT="" CONFIG="/etc/myapp.conf" DRY_RUN=0 # ---------- オプション処理 ---------- while true; do case "$1" in -v|--verbose) VERBOSE=1; shift ;; -o|--output) OUTPUT="$2"; shift 2 ;; -c|--config) case "$2" in ""|--*) shift ;; *) CONFIG="$2"; shift 2 ;; esac ;; -n|--dry-run) DRY_RUN=1; shift ;; -h|--help) usage; exit 0 ;; --) shift; break ;; *) echo "Internal error" >&2; exit 1 ;; esac done # ---------- 必須チェック ---------- if [[ -z "$OUTPUT" ]]; then echo "Error: --output=FILE は必須です" >&2 usage >&2 exit 1 fi # ---------- デバッグ出力 ---------- if (( VERBOSE )); then echo "[INFO] verbose モード有効" echo "[INFO] output=${OUTPUT}" echo "[INFO] config=${CONFIG}" echo "[INFO] dry_run=${DRY_RUN}" echo "[INFO] remaining args: $*" fi # ---------- ここから本処理 ---------- if (( DRY_RUN )); then echo "[DRY-RUN] 処理を実行せず終了します" exit 0 fi echo "処理を開始します: output=${OUTPUT}" # ... 本処理をここに記述 ...

よくあるトラブルと対処法

1. getoptが「invalid option」を返す

短形式の定義文字列(-o の後ろ)に余計なスペースや誤字があると発生します。vo:c:: のようにスペースなしで連結されているか確認してください。

2. eval set -- "$ARGS" 後に引数の順番がおかしい

GNU getopt はオプションを引数リストの先頭に、非オプション引数を末尾に並べ直します(-- で区切られる)。これは意図した動作です。-- を検出したら shift; break で残りが $@ に入ります。

3. 任意引数(::)がうまく取れない

長形式の任意引数は --config=value(等号あり)でないと値が渡りません。--config value(スペース区切り)では値として認識されず空になります。--config 単独で指定するとデフォルト動作にしたい場合は、必ず = 付きの書き方をドキュメントに明記しましょう。

4. スクリプトが途中で終了する(set -e と組み合わせ時)

getopt は無効なオプションを受け取ると終了コード 1 で終了します。set -e 環境では即座にスクリプト全体が終了するため、|| { usage >&2; exit 1; } のように明示的にエラーハンドリングするのが重要です。

本記事のまとめ

やりたいこと 方法
ロングオプションを定義する getopt --long verbose,output:,config::
引数なしフラグを定義する verbose(コロンなし)
必須引数を定義する output:(コロン1つ)
任意引数を定義する config::(コロン2つ)
スペース含む引数を安全に処理する eval set -- "$ARGS" パターン
GNU getopt かどうか確認する getopt --version で util-linux を確認
不明オプションでエラー終了する getopt ... || { usage >&2; exit 1; }
getopts の手軽さはそのままに、getopt でロングオプションに対応することで、スクリプトはツールとしての品質を大きく高められます。--help・--dry-run・--output=FILE のような読みやすいインターフェースは、初めて使うメンバーでもオプションを誤解しにくく、現場での運用コストを下げます。今回紹介した完成形テンプレートを起点に、プロジェクト固有のオプションを追加していくと、再利用性の高い運用スクリプトに育てていけます。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、シェルスクリプト実践講座の詳細はこちら>>。設計パターンからトラブルシュートまで、ハンズオンで習得できるカリキュラムを用意しています。

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。