シェルスクリプト(Bash)を実行する際、処理対象のファイル名や設定値、実行オプションなどをコマンドライン引数として渡すことで、スクリプトの汎用性が劇的に向上します。
引数の処理には、位置パラメータ($1, $2 …)や引数の個数を表す $#、全引数を展開する $@ と $* など、シェル特有の記法が存在します。また、引数が渡されなかった場合のデフォルト値代入(${1:-default})や引数チェック(バリデーション)は、実務で堅牢なスクリプトを書く上で欠かせない必須テクニックです。
この記事では、シェルスクリプトにおける引数処理の基礎から、$@ と $* の挙動の違い、実務でコピペしてそのまま使える実践テンプレートまでを分かりやすく解説します。
【早見表】シェルスクリプトの引数に関する特殊変数一覧
シェルスクリプト内で引数や実行状態を扱うための主な特殊変数は以下の通りです。
| 変数 / 記法 | 意味・役割 | 具体例 / 出力イメージ |
|---|---|---|
$0 |
スクリプト名(または実行パス) | ./backup.sh |
$1, $2 … $9 |
第1引数、第2引数 … 第9引数 | 1番目、2番目の引数値 |
${10}, ${11} … |
第10引数以降(波括弧 {} が必須) |
10番目以降の引数値 |
$# |
渡された引数の総数 | 引数が3個なら 3 |
"$@" |
全引数を個別にクォートして展開(推奨) | "arg1" "arg2" "arg with space" |
"$*" |
全引数を1つの文字列として連結して展開 | "arg1 arg2 arg with space" |
${1:-default} |
$1 が未設定・空文字なら default を使用 |
未指定時のフォールバック値 |
shift |
位置パラメータを左に1つシフト($2 が $1 に繰り上がる) |
引数を先頭から順次消費する処理 |
$? |
直前に実行したコマンドの終了ステータス | 成功時は 0、エラー時は 1 以上 |
1. 位置パラメータの基本(1. 位置パラメータの基本($0, $1, $2 …)
, , …)
シェルスクリプトに渡された引数は、渡された順番に応じて 位置パラメータ(Positional Parameters) と呼ばれる特殊な変数に格納されます。
基本的な受け取り方とスクリプト例
以下は、第1引数と第2引数を受け取って表示するシンプルな例です。
#!/usr/bin/env bash
set -euo pipefail
echo "スクリプト名 (\$0): $0"
echo "第1引数 (\$1): $1"
echo "第2引数 (\$2): $2"
実行例:
$ ./sample.sh apple banana
スクリプト名 ($0): ./sample.sh
第1引数 ($1): apple
第2引数 ($2): banana
注意:10個以上の引数は波括弧 が必須
引数が10個以上ある場合、$10 と書くとシェルは $1 の展開結果 + 文字列 "0" として解釈してしまいます。
# NGな例($1の値の後ろに "0" がくっつく)
echo "$10"
# OKな例(波括弧で囲む)
echo "${10}"
echo "${11}"
実務ではバグを防ぐため、1桁の引数でも "${1}" のように波括弧を付ける習慣をつけておくと安全です。
2. 【最重要】「$@」と「$*」の違いとクォーテーションの挙動
すべての引数をまとめて扱いたい場合、$@ または $* を使用します。一見同じように見えますが、ダブルクォートで囲んだときの展開方法が大きく異なります。
展開結果の違い一覧
| 記法 | 展開の挙動 | スペース入り引数の扱い | 実務での推奨度 |
|---|---|---|---|
"$@"(ダブルクォートあり) |
"$1" "$2" "$3" ... と各引数を個別にクォート保持 |
安全(空白を維持したまま1引数として扱う) | 最推奨(標準) |
"$*"(ダブルクォートあり) |
"$1c$2c$3" と全引数をIFS(通常は空白)で結合した1つの文字列 |
1つの巨大な文字列に合体する | 全体を1行で出力・ログ記録する際に使用 |
$@(クォートなし) |
単語分割(Word Splitting)が発生 | 危険(スペースで勝手に分割される) | 非推奨 |
$*(クォートなし) |
単語分割(Word Splitting)が発生 | 危険(スペースで勝手に分割される) | 非推奨 |
実機で検証:スペース入り引数をループ処理する場合
空白を含む引数(例: "Tokyo Tower", "Mount Fuji")を渡して、"$@" と "$*" の挙動を比較してみましょう。
#!/usr/bin/env bash
echo "=== "\$@" の場合(各引数が独立して保持される) ==="
for item in "$@"; do
echo "要素: [$item]"
done
echo ""
echo "=== "\$*" の場合(全引数が1つの文字列に結合される) ==="
for item in "$*"; do
echo "要素: [$item]"
done
実行結果:
$ ./compare.sh "Tokyo Tower" "Mount Fuji"
=== "$@" の場合(各引数が独立して保持される) ===
要素: [Tokyo Tower]
要素: [Mount Fuji]
=== "$*" の場合(全引数が1つの文字列に結合される) ===
要素: [Tokyo Tower Mount Fuji]
このように、"$@" は引数の境界を正しく維持してループを2回実行しますが、"$*" は全体が1つの文字列にまとめられて1回しかループしません。
実務の鉄則:
複数の引数をループで回す場合や、別のコマンドにそのまま引き渡す場合は、必ず"$@"(ダブルクォート付き) を使用してください。
3. 引数チェックとバリデーション(未指定時のエラー処理)
必要な引数が指定されずにスクリプトが実行されると、意図しない動作やデータ破損の原因になります。スクリプト冒頭で必ず引数チェックを行いましょう。
パターン1: 引数の個数($#)をチェックする定番書き方
引数の総数を表す $# を使い、期待する個数未満であればエラーメッセージと「使い方(Usage)」を表示して終了(exit 1)します。
#!/usr/bin/env bash
set -euo pipefail
# 必須引数が2個未満ならUsageを表示して終了
if [ "$#" -lt 2 ]; then
echo "エラー: 引数が不足しています。" >&2
echo "使い方: $0 <入力ファイル> <出力ディレクトリ>" >&2
exit 1
fi
INPUT_FILE="$1"
OUTPUT_DIR="$2"
echo "入力: $INPUT_FILE"
echo "出力先: $OUTPUT_DIR"
パターン2: 引数が空文字でないかチェックする(-z / -n)
変数が空文字かどうかは [ -z "$1" ](空なら真)または [ -n "$1" ](空でなければ真)で判定します。
if [ -z "${1:-}" ]; then
echo "エラー: 第1引数に対象ユーザー名を指定してください。" >&2
exit 1
fi
パターン3: ファイルやディレクトリの存在チェック
渡された引数が実在するファイル・ディレクトリであるかを検証します。
FILE_PATH="$1"
if [ ! -f "$FILE_PATH" ]; then
echo "エラー: ファイルが存在しません -> $FILE_PATH" >&2
exit 1
fi
4. デフォルト値(初期値)の設定テクニック
引数が省略された場合にデフォルトの値を使いたい場合は、Bashの パラメータ展開 機能を活用すると、if 文を書かずに1行でスマートに記述できます。
記法1: ${1:-default}(未指定・空ならデフォルト値を使用)
第1引数が指定されていない(または空文字)の場合に、既定の値を代入します。
#!/usr/bin/env bash
# 第1引数が指定されなければ "development"、第2引数がなければ "8080"
ENV_NAME="${1:-development}"
PORT="${2:-8080}"
echo "環境: $ENV_NAME"
echo "ポート: $PORT"
実行例:
$ ./server.sh
環境: development
ポート: 8080
$ ./server.sh production 443
環境: production
ポート: 443
記法2: ${1:?エラーメッセージ}(未指定なら即エラー終了)
必須の引数が渡されなかった場合に、自動的にエラーメッセージを標準エラー出力へ吐き出してスクリプトを終了させることができます。
#!/usr/bin/env bash
TARGET_HOST="${1:?エラー: 対象ホスト名を第1引数に指定してください}"
echo "Connecting to $TARGET_HOST..."
実行例(引数なしで実行した場合):
$ ./deploy.sh
./deploy.sh: line 3: 1: エラー: 対象ホスト名を第1引数に指定してください
5. shift コマンドで引数を順次処理する
shift は、位置パラメータを左に1つずらす(シフトする)コマンドです。
shift を実行すると、$1 が破棄され、$2 が新たな $1、$3 が新たな $2 に繰り上がります($# の値も1減ります)。
while ループと shift による引数処理
引数の数が可変である場合、while [ "$#" -gt 0 ] と組み合わせて先頭から順番に引数を処理できます。
#!/usr/bin/env bash
echo "処理開始(引数の総数: $#)"
while [ "$#" -gt 0 ]; do
echo "処理中: $1 (残り引数: $#)"
# 次の引数へシフト
shift
done
echo "処理完了"
実行例:
$ ./shift_demo.sh red green blue
処理開始(引数の総数: 3)
処理中: red (残り引数: 3)
処理中: green (残り引数: 2)
処理中: blue (残り引数: 1)
処理完了
※ shift 2 のように数値を渡すと、一度に複数個の引数をシフトすることも可能です。
6. 実務で即使える!引数処理の実践テンプレート
実務で頻出する引数処理の完成版テンプレートです。コピー&ペーストしてカスタマイズしてご利用ください。
テンプレート1: 引数バリデーション+Usage関数付き標準スクリプト
#!/usr/bin/env bash
set -euo pipefail
# 使い方を表示する関数
usage() {
cat << EOF
使い方: $(basename "$0") [オプション] <対象ファイル> [出力形式]
説明:
指定したファイルを解析し、指定されたフォーマットで出力します。
引数:
対象ファイル 解析対象のファイルパス(必須)
出力形式 json | csv | text(省略時: text)
オプション:
-h, --help ヘルプを表示
EOF
exit 1
}
# ヘルプオプションの簡易判定
if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then
usage
fi
# 引数個数チェック
if [ "$#" -lt 1 ]; then
echo "エラー: 引数が不足しています。" >&2
usage
fi
# 引数の受け取りと初期値
TARGET_FILE="$1"
OUTPUT_FORMAT="${2:-text}"
# ファイル存在チェック
if [ ! -f "$TARGET_FILE" ]; then
echo "エラー: ファイルが見つかりません -> $TARGET_FILE" >&2
exit 1
fi
echo "=== 処理実行 ==="
echo "対象: $TARGET_FILE"
echo "フォーマット: $OUTPUT_FORMAT"
テンプレート2: サブコマンド分岐(start / stop / status)
Dockerやsystemctlのように、第1引数でコマンドを切り替える構成です。
#!/usr/bin/env bash
set -euo pipefail
ACTION="${1:-}"
case "$ACTION" in
start)
echo "サービスを起動します..."
;;
stop)
echo "サービスを停止します..."
;;
status)
echo "サービスの稼働状況を確認します..."
;;
*)
echo "エラー: 不正なアクションです -> '${ACTION}'" >&2
echo "利用可能なコマンド: start | stop | status" >&2
exit 1
;;
esac
7. さらに発展:オプション引数(-a, -f など)を扱いたい場合
スクリプトが複雑になり、順不同のオプションフラグ(例: -v で詳細出力、-f filename でファイル指定)を扱いたい場合は、シェル組み込みの getopts コマンドを使用するのが標準的です。
オプション解析の詳しい実装方法については、以下の解説記事も合わせて参考にしてください。
よくある質問(FAQ)
Q1. 関数(Function)の中で や $@ を使うとどうなりますか?
Bashでは、関数内部の位置パラメータはその関数に渡された引数に上書きされます。スクリプト全体への引数を関数内で参照したい場合は、関数外であらかじめ別名変数(例: SCRIPT_ARG1="$1")に退避させておくか、関数呼び出し時に引数として渡す必要があります。
Q2. 引数にスペースが含まれている場合、どうやってスクリプトを実行すればよいですか?
実行時にダブルクォートまたはシングルクォートで引数を囲みます。
例: ./script.sh "My Documents/report.pdf"
スクリプト側では必ず "$1" や "$@" のようにダブルクォートで変数を受け取るようにしてください。
Q3. 引数がない場合にスクリプト内で対話的に入力を受け取ることはできますか?
はい、read コマンドと組み合わせることで、「引数が指定されていればそれを使い、なければ対話的にプロンプトを表示して入力させる」という設計が可能です。
USER_NAME="${1:-}"
if [ -z "$USER_NAME" ]; then
read -rp "ユーザー名を入力してください: " USER_NAME
fi
echo "Hello, $USER_NAME!"
まとめ
シェルスクリプトにおける引数処理のポイントをまとめます。
- 位置パラメータ:
$1,$2で受け取る(10番目以降は${10}と波括弧が必須) - 全引数の展開: ループ処理やコマンド引き渡しには必ず
"$@"(ダブルクォート付き) を使う - バリデーション: スクリプト冒頭で
$#の個数チェックと Usage 表示を行う - デフォルト値:
${1:-初期値}を使って簡潔にフォールバック設定を行う - 順次処理:
shiftを使って引数を先頭から1つずつ安全に消費する
引数を柔軟かつ堅牢に処理できるようにすることで、チーム内でも再利用しやすい実用的なシェルスクリプトを構築できます。ぜひ本記事のテンプレートを活用してみてください。

コメント