シェルスクリプトで引数を扱う際、$@ や $* は頻繁に登場します。しかし、クォートの有無によって挙動が大きく変わるため、正確に理解していないとバグの原因になります。本記事では、それぞれの違いを具体例とともに整理します。
前提:位置パラメータとは
シェルスクリプトにおいて、コマンドライン引数は以下のように扱われます。
./script.sh foo bar "baz qux"
このとき:
$1→foo$2→bar$3→baz qux
そして、すべての引数をまとめて扱うのが $@ と $* です。
結論サマリ
| 書き方 | 意味 | 挙動 |
|---|---|---|
$@ | 全引数 | 単語分割される |
$* | 全引数 | 単語分割される |
"$@" | 全引数 | 各引数を個別に保持(最重要) |
"$*" | 全引数 | 1つの文字列に結合 |
$@ と $*(クォートなし)
for arg in $@; do
echo "$arg"
done
for arg in $*; do
echo "$arg"
done
挙動
どちらもほぼ同じで、「空白で分割された単語列」として展開されます。
問題点
./script.sh "hello world"
この場合:
-
"hello world"が 2つに分割されてしまうhelloworld
つまり、元の引数の構造が壊れるため、実務ではほぼ使いません。
"$@"(最も重要)
for arg in "$@"; do
echo "$arg"
done
挙動
- 各引数をそのまま1つの要素として保持
- スペースを含む引数も安全に扱える
例
./script.sh foo "bar baz"
出力:
foo
bar baz
特徴
- 引数の数を保持
- 各引数が独立して扱われる
👉 シェルスクリプトで引数を扱うときは基本これを使う
"$*"(1つの文字列に結合)
echo "$*"
挙動
- すべての引数を 1つの文字列に結合
- 区切り文字は
IFS(デフォルトはスペース)
例
./script.sh foo "bar baz"
出力:
foo bar baz
注意点
- 引数の区切りが失われる
- 個別処理には向かない
違いを図で理解
入力:
./script.sh A "B C" D
展開結果
| 書き方 | 展開結果 |
|---|---|
$@ | A B C D |
$* | A B C D |
"$@" | ”A” “B C” “D” |
"$*" | ”A B C D” |
よくあるバグパターン
❌ NG例
for arg in $@; do
cp "$arg" /dest/
done
→ スペースを含むファイル名で壊れる
✅ 正しい書き方
for arg in "$@"; do
cp "$arg" /dest/
done
実務での使い分け
基本ルール
- 迷ったら
"$@"を使う $@,$*(クォートなし)は基本避ける"$*"は「ログ出力などでまとめたいとき」に限定
まとめ
$@/$*はクォートなしだと危険(単語分割される)"$@"は安全に引数を扱える(最重要)"$*"はすべてを1つの文字列にまとめる
👉 シェルスクリプトで引数処理を書くなら "$@" をデフォルトにするべき
補足:なぜ違いがあるのか
これはシェルの「単語分割(word splitting)」と「クォート」の仕様に由来します。
特に "$@" は特殊な展開で、各引数が個別に保持される点が他と決定的に異なります。