強力なスクリプトのためのBash引数解析マスターガイド

位置パラメータ、getopts、長いオプションループ、デフォルト値、明確な使用法エラーを使ってBash引数を解析します。

強力なスクリプトのためのBash引数解析マスターガイド

コマンドライン引数は、Bashスクリプトを柔軟にするものです。Bashの引数解析が弱いと、異なるファイル、ホスト、モードが必要になるたびに、スクリプト内の変数を編集しなければならなくなります。

このガイドでは、位置引数、getoptsを使った短いオプション、while/caseループを使った長いオプションの扱い方を説明します。目標は、不正な入力を早期に拒否し、実行方法を正確に教えてくれるスクリプトを作成することです。

基本:位置引数

フラグや名前付きオプションに取り組む前に、Bashが単純な順序引数をどのように扱うかを理解することが重要です。

変数 説明 ./script.sh foo barとして呼び出した場合の例
$0 スクリプト自体の名前 ./script.sh
$1, $2, ... 1番目、2番目などの位置引数 foo, bar
$# 位置引数の数 2
$@ すべての位置引数(個別の文字列として扱われる) foo bar
$* すべての位置引数(単一の文字列として扱われる) foo bar

例:単純な位置チェック

#!/bin/bash

if [ "$#" -ne 2 ]; then
    echo "使い方: $0 <ソースファイル> <宛先ディレクトリ>"
    exit 1
fi

SOURCE="$1"
DESTINATION="$2"

echo "$SOURCE を $DESTINATION にコピー中..."
# cp "$SOURCE" "$DESTINATION"

getoptsを使った標準的な解析(短いオプション用)

-v(詳細)や-f <ファイル>のようなオプションを必要とするプロフェッショナルなスクリプトには、純粋なBashで標準的かつ最も信頼性の高い方法として、組み込みのgetoptsユーティリティがあります。これは特に短い(1文字の)オプション用に設計されています。

getoptsの仕組み

getoptsはオプションを順次読み取り、指定された変数(通常はOPT)に見つかったオプションを設定し、引数が必要な場合はその値を変数OPTARGに格納します。

  • オプション文字列(OPTSTRING): 有効なオプションを定義します。オプションが引数を必要とする場合(例:-f ファイル名)、文字の後にコロン(:)を付けます。例:vho:-v-h、および値を必要とする-oを許可します。
  • OPTIND 次に処理する引数を追跡する内部Bashインデックス。1に初期化する必要があります(デフォルトですが、複雑なスクリプトではリセットされることがあります)。

実用的なgetoptsテンプレート

このテンプレートは、-v(フラグ)、-h(フラグ)、-f(値を必要とするオプション)の3つのオプションを処理します。

#!/bin/bash

# --- デフォルト値 ---
VERBOSE=0
FILENAME="default.txt"

# --- 使用法関数 ---
usage() {
    echo "使い方: $0 [-v] [-h] [-f <ファイル>] <入力>"
    exit 1
}

# --- 引数解析ループ ---
while getopts ":vhf:" OPT; do
    case "$OPT" in
        v)
            VERBOSE=1
            echo "詳細モードが有効になりました。"
            ;;
        h)
            usage
            ;;
        f)
            # OPTARGには-fに指定された引数が格納される
            FILENAME="$OPTARG"
            echo "出力ファイル名が設定されました: $FILENAME"
            ;;
        \?)
            # 認識されないオプションを処理
            echo "エラー: 無効なオプション -$OPTARG" >&2
            usage
            ;;
        :)
            # 引数が必要なオプションに引数がない場合を処理(例:ファイル名なしの-f)
            echo "エラー: オプション -$OPTARG には引数が必要です。" >&2
            usage
            ;;
    esac
done

# --- 位置引数のシフト ---
# getoptsが終了した後、OPTINDは最初の非オプション引数のインデックスを保持します。
# shiftを使用して、解析済みのオプションをすべて破棄し、位置引数($1、$2など)のみを残します。
shift $((OPTIND - 1))

# 必須の位置引数(INPUT)が存在するか確認
INPUT_DATA="$1"
if [ -z "$INPUT_DATA" ]; then
    echo "エラー: 入力データが必要です。"
    usage
fi

echo "---"
echo "入力の処理: $INPUT_DATA"
echo "詳細ステータス: $VERBOSE"

ヒント: while getoptsループの直後にshift $((OPTIND - 1))を常に使用して、オプションを残りの位置引数からきれいに分離してください。

--optionのような長いオプションの処理

純粋なBashの組み込みgetoptsは短いオプションのみをサポートします。現代的な長いオプション(例:--verbose--output-file=data.log)を処理するには、shiftコマンドを使用してwhilecaseを使ったカスタム解析ループを実装する必要があります。

この方法では、より明示的な引数管理が必要です。

カスタム長いオプションパーサー

#!/bin/bash

# --- デフォルト値 ---
VERBOSE=0
OUTPUT_FILE=""

# 使用法を表示するカスタム関数(簡略化のため省略)
# usage() { ... }

while [ "$#" -gt 0 ]; do
    case "$1" in
        --verbose)
            VERBOSE=1
            shift
            ;;
        --output-file)
            # フラグと値の2回のシフトが必要
            if [ -z "${2:-}" ]; then
                echo "エラー: --output-file には値が必要です。"
                exit 1
            fi
            OUTPUT_FILE="$2"
            shift 2
            ;;
        --help)
            usage
            ;;
        -*)
            echo "エラー: 不明なオプション $1" >&2
            exit 1
            ;;
        *)
            # 最初の位置引数を見つけたら、オプションの解析を停止
            break
            ;;
    esac
done

# 残りの引数は$1、$2などとして利用可能
# ... スクリプトロジックの続き ...

if [ "$OUTPUT_FILE" ]; then
    echo "データは $OUTPUT_FILE にリダイレクトされました"
fi

スクリプトがオプションの後に位置引数を受け入れる場合は、--または最初の非オプション引数に達するまで解析を続けます。一般的な慣習として、--は「オプションの解析をここで停止する」ことを意味します。

while [ "$#" -gt 0 ]; do
    case "$1" in
        --)
            shift
            break
            ;;
        --verbose)
            VERBOSE=1
            shift
            ;;
        *)
            break
            ;;
    esac
done

高度:キーと値の長いオプションの処理(--key=value

引数が等号を使って渡される標準的なUNIXスタイルを好む場合は、パラメータ置換を使用して引数を分割する必要があります。

while [ "$#" -gt 0 ]; do
    case "$1" in
        --limit=*)
            LIMIT_VAL="${1#*=}" # '='までのすべてを削除
            echo "制限が設定されました: $LIMIT_VAL"
            shift
            ;;
        # ... その他のオプション ...
    esac
done

堅牢なスクリプト作成のためのベストプラクティス

A. 短いオプションと長いオプションの組み合わせ

最大の柔軟性を得るために、経験豊富なスクリプターは、短いオプションにはgetoptsの信頼性を、長いオプションにはカスタムのwhile/caseループを組み合わせることがよくあります。長いオプションのループが最初に実行され、長いフラグを消費し、その後、残りの引数(短いオプションを含む)がgetoptsによって処理されます。

ただし、よりクリーンで一般的なパターンは、-o--optionの両方を探し、それに応じてシフトする1つの堅牢なカスタムループですべての引数を処理することです。

B. エラーハンドリングと使用法

スクリプトの必須引数とオプションを説明する明確なusage関数を常に提供してください。この関数は次の場合に呼び出されるべきです。

  1. ユーザーがヘルプを要求した場合(例:-hまたは--help)。
  2. 必須の引数が不足している場合。
  3. 無効または不明なオプションが指定された場合。

エラー時にはスクリプトがゼロ以外のステータスで終了し(exit 1)、エラーメッセージを標準エラー出力(>&2)に出力することを確認してください。

C. デフォルト値

スクリプトの先頭で、すべての設定変数を適切なデフォルト値で初期化します。これにより、オプションの引数が渡されなくてもスクリプトが予測可能になります。

# 解析前に常に変数を初期化
LOG_LEVEL="info"
FORCE=0

まとめ

単純な必須値には位置引数、ポータブルな短いオプションにはgetopts、長いオプションが必要な場合にはカスタムのwhile/caseループを使用してください。スクリプトを自動化で信頼する前に、欠落した値、不明なフラグ、スペースを含む引数をテストしてください。