getoptsコマンドでbashスクリプトの引数を処理する方法|オプション解析と実践例も

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Linuxtips, シェルスクリプト > getoptsコマンドでbashスクリプトの引数を処理する方法|オプション解析と実践例も
「シェルスクリプトにオプションを渡したいけど、$1・$2で受け取ると順番が変わったら壊れる…」
そういう悩みを抱えているサーバー管理者は多いです。

bashスクリプトを書き始めると、最初は位置パラメータ($1、$2…)で引数を受け取ります。しかしスクリプトが育ってきて「-v(詳細表示)」「-o 出力先」「-n(ドライラン)」といったオプションが増えてくると、位置パラメータでの管理はすぐに破綻します。

この記事では、bashの組み込みコマンド getopts を使ったオプション解析の方法を解説します。
基本的な1文字オプションの受け取り方から、引数付きオプション、エラー処理、実務で使えるスクリプト例まで、RHEL 9.4 / Ubuntu 24.04 LTS で動作確認した手順でお伝えします。

この記事のポイント

・getopts は bash 組み込みのオプション解析コマンドで外部コマンド不要
・while getopts "ab:c" opt; do case $opt in で1文字オプションを処理できる
・コロン付き(b:)は引数ありオプション、$OPTARG で値を取得する
・先頭コロン(":ab:")でエラーを自前処理してサイレントモードにできる


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

getoptsとは何か、getoptとの違い

getopts は bash の組み込みコマンドです。command -v getopts を実行しても外部ファイルのパスは返らず、getopts is a shell builtin と表示されます。
似た名前の getopt(末尾に s がない)は外部コマンドで、長いオプション(--verbose など)を扱える一方、OS によって挙動が異なり移植性に課題があります。
比較 getopts(bash組み込み) getopt(外部コマンド)
長いオプション 非対応 対応(--verbose 等)
移植性 高い(POSIX準拠) OS 差異あり
インストール不要 はい util-linux パッケージ必要
実務での用途 社内スクリプト・自動化 汎用 CLI ツール作成
社内向けのサーバー管理スクリプトであれば getopts で十分です。

getoptsの基本的な書き方

1. オプション文字列の読み方

getopts の第1引数は「オプション文字列」です。

# オプション文字列の例 # "abc" → -a -b -c を受け取る(引数なし) # "ab:c" → -a -b 値 -c を受け取る(b は引数あり) # ":ab:c" → 先頭のコロンはサイレントモード(エラー処理を自前で行う)

コロン : が文字の直後に付いている場合、そのオプションは引数を必要とします。
先頭に : が付いている場合は「サイレントモード」となり、エラーメッセージを getopts 自身が出力せず、スクリプト側で制御できます。

2. while ループと case 文の組み合わせ

getopts は1回の呼び出しで1つのオプションを処理します。while ループで繰り返し呼び出すのが定石です。

#!/bin/bash # 基本的な getopts の書き方 while getopts "vn:o:" opt; do case $opt in v) VERBOSE=true ;; n) NAME="$OPTARG" ;; o) OUTPUT="$OPTARG" ;; \?) echo "不明なオプション: -$OPTARG" >&2 exit 1 ;; esac done # オプション以外の残り引数は $@ に残る shift $((OPTIND - 1)) echo "NAME=$NAME, OUTPUT=$OUTPUT, VERBOSE=$VERBOSE" echo "残り引数: $@"

重要な変数が3つあります。
$OPTARG:引数ありオプション(コロン付き)で、その引数の値が格納されます
$OPTIND:次に処理するべき引数のインデックス。shift $((OPTIND - 1)) で残り引数を $@ に整理できます
\?:未定義のオプションが指定された場合に一致するパターンです

3. 実際に動かす

上記スクリプトを test-opts.sh として保存して実行します。

# 実行権限を付与 $ chmod +x test-opts.sh # オプションを渡して実行 $ ./test-opts.sh -v -n "webserver01" -o /tmp/result.log NAME=webserver01, OUTPUT=/tmp/result.log, VERBOSE=true 残り引数: # 残り引数(ファイル名など)も渡す場合 $ ./test-opts.sh -n "db01" /etc/hosts /etc/hostname NAME=db01, OUTPUT=, VERBOSE= 残り引数: /etc/hosts /etc/hostname

引数ありオプションのエラー処理

1. オプションの引数が省略された場合

コロン付きオプションに引数を与えずに実行すると、getopts はエラーを報告します。

# -n オプションの引数を省略した場合(通常モード) $ ./test-opts.sh -n ./test-opts.sh: option requires an argument -- 'n' 不明なオプション: -

サイレントモード(先頭コロン)を使うと、エラーを自前でハンドリングできます。

#!/bin/bash # サイレントモード(先頭に : を付ける) while getopts ":vn:o:" opt; do case $opt in v) VERBOSE=true ;; n) NAME="$OPTARG" ;; o) OUTPUT="$OPTARG" ;; :) # 引数が必要なオプションに引数が与えられなかった場合 echo "エラー: -$OPTARG には引数が必要です" >&2 exit 1 ;; \?) echo "エラー: 不明なオプション -$OPTARG" >&2 exit 1 ;; esac done

: のパターンが追加されています。このパターンは「引数が必要なオプションに引数が与えられなかった」場合に一致します。

2. usageメッセージを関数化する

実務では usage 表示を関数として定義しておくと、複数箇所から呼び出せて便利です。

#!/bin/bash usage() { echo "使い方: $0 [-v] [-n ホスト名] [-o 出力先]" >&2 echo "" echo " -v 詳細表示モード" >&2 echo " -n ホスト名 対象ホスト(必須)" >&2 echo " -o 出力先 ログの保存先(デフォルト: /tmp/result.log)" >&2 exit 1 } OUTPUT="/tmp/result.log" # デフォルト値 while getopts ":vn:o:" opt; do case $opt in v) VERBOSE=true ;; n) NAME="$OPTARG" ;; o) OUTPUT="$OPTARG" ;; :) echo "エラー: -$OPTARG には引数が必要です" >&2; usage ;; \?) echo "エラー: 不明なオプション -$OPTARG" >&2; usage ;; esac done # 必須オプションのチェック if [ -z "$NAME" ]; then echo "エラー: -n オプション(ホスト名)は必須です" >&2 usage fi echo "ホスト: $NAME, 出力: $OUTPUT, 詳細: ${VERBOSE:-false}"

実務で使えるスクリプト例

1. サーバー死活確認スクリプト

複数ホストへの ping チェックを行うスクリプトです。オプションでタイムアウトや繰り返し回数を変更できます。

#!/bin/bash # check-hosts.sh: サーバー死活確認スクリプト # 使い方: ./check-hosts.sh [-v] [-c 回数] [-t タイムアウト秒] ホスト名 [ホスト名...] COUNT=3 TIMEOUT=5 VERBOSE=false usage() { echo "使い方: $0 [-v] [-c 回数] [-t タイムアウト秒] ホスト名..." >&2 exit 1 } while getopts ":vc:t:" opt; do case $opt in v) VERBOSE=true ;; c) COUNT="$OPTARG" ;; t) TIMEOUT="$OPTARG" ;; :) echo "エラー: -$OPTARG には引数が必要です" >&2; usage ;; \?) echo "エラー: 不明なオプション -$OPTARG" >&2; usage ;; esac done shift $((OPTIND - 1)) # ホスト名がなければエラー if [ $# -eq 0 ]; then echo "エラー: チェック対象のホスト名を指定してください" >&2 usage fi # 各ホストに ping for host in "$@"; do if ping -c "$COUNT" -W "$TIMEOUT" "$host" > /dev/null 2>&1; then echo "[OK] $host は応答しています" if "$VERBOSE"; then ping -c 1 "$host" | grep "time=" fi else echo "[FAIL] $host は応答しません" fi done

実行例をサーバー上で確認すると以下のようになります。

# 基本実行 $ ./check-hosts.sh web01.example.local db01.example.local [OK] web01.example.local は応答しています [OK] db01.example.local は応答しています # -v オプションで詳細表示 $ ./check-hosts.sh -v -c 1 web01.example.local [OK] web01.example.local は応答しています 64 bytes from 192.168.10.11: icmp_seq=1 ttl=64 time=0.312 ms # 存在しないホスト $ ./check-hosts.sh -t 2 backup01.example.local [FAIL] backup01.example.local は応答しません

2. ディレクトリ使用量レポートスクリプト

指定ディレクトリ配下の使用量を調べて、しきい値を超えたものを警告します。
Linux 基本コマンドの解説で触れているように、du や ls といった基本コマンドをスクリプト化する際に getopts が役立ちます。

#!/bin/bash # disk-report.sh: ディレクトリ使用量レポート # 使い方: ./disk-report.sh [-d 検索ディレクトリ] [-s しきい値MB] [-v] TARGET_DIR="/var" THRESHOLD=500 VERBOSE=false while getopts ":d:s:v" opt; do case $opt in d) TARGET_DIR="$OPTARG" ;; s) THRESHOLD="$OPTARG" ;; v) VERBOSE=true ;; :) echo "エラー: -$OPTARG には引数が必要です" >&2; exit 1 ;; \?) echo "エラー: 不明なオプション -$OPTARG" >&2; exit 1 ;; esac done echo "=== ディスク使用量レポート ===" echo "対象: $TARGET_DIR / しきい値: ${THRESHOLD}MB" echo "" # しきい値(MB)を超えるディレクトリを表示 du -mx "$TARGET_DIR" --max-depth=2 2>/dev/null \ | awk -v threshold="$THRESHOLD" '$1 > threshold {print $0}' \ | sort -rn \ | while read size path; do echo "[警告] ${size}MB $path" done if "$VERBOSE"; then echo "" echo "--- 上位10件(全体) ---" du -mx "$TARGET_DIR" --max-depth=2 2>/dev/null | sort -rn | head -10 fi

getoptsで陥りやすいトラブルと対処法

1. shift $((OPTIND - 1)) を忘れた場合

getopts はオプションを処理しても $1 以降の引数を自動でシフトしません。
shift $((OPTIND - 1)) を忘れると、while ループ後でも $@ にオプション文字が残ったままになります。

# OPTIND を確認する $ cat debug-opts.sh #!/bin/bash while getopts "vn:" opt; do case $opt in v) echo "OPTIND=$OPTIND, opt=$opt" ;; n) echo "OPTIND=$OPTIND, opt=$opt, OPTARG=$OPTARG" ;; esac done echo "shift 前: $@" shift $((OPTIND - 1)) echo "shift 後: $@" $ ./debug-opts.sh -v -n server01 file1.txt file2.txt OPTIND=2, opt=v OPTIND=4, opt=n, OPTARG=server01 shift 前: -v -n server01 file1.txt file2.txt shift 後: file1.txt file2.txt

2. オプション引数にハイフンから始まる値を渡してしまう

-n -verbose のように渡すと、getopts-verbose をオプションとして解釈しようとしてエラーになります。
引数の値がハイフンから始まる可能性がある場合は、-n="-verbose"--(オプション終了マーカー)を使う回避策を検討してください。

3. 関数の中で getopts を使う場合の注意

関数内で getopts を呼ぶ場合、$OPTIND はグローバル変数のため、関数入口で local OPTIND=1 と宣言することで意図しない動作を防げます。

# 関数内で getopts を使う正しい書き方 parse_options() { local OPTIND=1 # ★ 必須:関数内でリセット local opt while getopts "vn:" opt; do case $opt in v) echo "verbose mode" ;; n) echo "name=$OPTARG" ;; esac done } parse_options -v -n test01 parse_options -n server02

4. 「option requires an argument」が出るパターン

コロン付きオプションに引数を与え忘れた場合に発生します。
Linux ポート確認の全コマンドのように、スクリプトでポート番号をオプション引数で受け取る場合は、数値バリデーションも合わせて実装しておくと安全です。

# ポート番号のバリデーション例 while getopts ":p:" opt; do case $opt in p) if ! [[ "$OPTARG" =~ ^[0-9]+$ ]] || [ "$OPTARG" -lt 1 ] || [ "$OPTARG" -gt 65535 ]; then echo "エラー: ポート番号は 1~65535 の整数で指定してください" >&2 exit 1 fi PORT="$OPTARG" ;; :) echo "エラー: -$OPTARG には引数が必要です" >&2; exit 1 ;; \?) echo "エラー: 不明なオプション -$OPTARG" >&2; exit 1 ;; esac done echo "指定ポート: $PORT"

本記事のまとめ

やりたいこと 書き方
引数なしオプション(-v)を受け取る while getopts "v" opt; do case $opt in v) …
引数ありオプション(-n 値)を受け取る while getopts "n:" opt、値は $OPTARG
不明なオプションを処理する case の \?) パターンで処理
引数省略エラーを自前処理する 先頭コロン ":n:" + case の :) パターン
オプション処理後に残り引数を取り出す shift $((OPTIND - 1))
関数内で getopts を使う 関数の冒頭で local OPTIND=1 を宣言
getopts を覚えると、「とりあえず動く一回きりのスクリプト」から「オプションで柔軟に動作を変えられる再利用可能なスクリプト」へ一段階昇格できます。
コロンの付け方と $OPTARG$OPTIND の役割さえ把握すれば、使いこなすのは難しくありません。

まずは手元のスクリプトで $1$2 による引数受け取りを getopts に書き換えてみてください。
シェルスクリプトのメンテナビリティが一気に上がります。

ls コマンドの基本オプションcsh 環境変数設定の解説のような基本コマンドを組み合わせて、本格的なシェルスクリプトを書いていきましょう。

「スクリプトが書けるLinuxエンジニア」になりたいなら

getopts を使いこなせると、メンテナブルな自動化スクリプトが書けるようになります。
ネットの古い情報をコピペするだけでなく、現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼントしています。

「独学の時間がもったいない」「プロから直接、現場の技術を最短で学びたい」という本気の方には、2日で実務レベルのスキルが身につく【初心者向けハンズオンセミナー】も開催しています。

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

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

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

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

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

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

登録10秒/合わなければ解除3秒 / 詳細はこちら

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

この記事を書いた人

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

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

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