这是一个Shell脚本中非常经典且重要的区别。
简单来说:
$*将所有的参数视为一个单一的字符串。$@将所有的参数视为独立的、分开的字符串。
这个区别只有在被双引号引用时("$*" 和 "$@")才会变得非常明显。如果不加引号,$* 和 $@ 的行为是相同的。
详细解释与示例
我们通过一个简单的脚本 test.sh 来演示:
#!/bin/bash
echo "Testing \$* and \$@"
echo "--- Without double quotes ---"
echo "Using \$*:"
for arg in $*; do
echo "[$arg]"
done
echo "Using \$@:"
for arg in $@; do
echo "[$arg]"
done
echo "--- With double quotes ---"
echo "Using \"\$*\":"
for arg in "$*"; do
echo "[$arg]"
done
echo "Using \"\$@\":"
for arg in "$@"; do
echo "[$arg]"
done
运行这个脚本并传递几个参数:
bash test.sh one two "three four"
输出结果分析:
Testing $* and $@
--- Without double quotes ---
Using $*:
[one]
[two]
[three]
[four]
Using $@:
[one]
[two]
[three]
[four]
分析(未加引号):
当不加双引号时,$* 和 $@ 的行为完全一样。它们都会经历“单词拆分”(Word Splitting)。参数 "three four" 作为一个带空格的字符串被传递进来,但 for 循环在遇到 $* 或 $* 时,会按空格将其拆分成两个独立的单词 three 和 four。这通常不是我们想要的行为。
--- With double quotes ---
Using "$*":
[one two three four]
Using "$@":
[one]
[two]
[three four]
分析(加引号):
这才是关键区别所在!
-
"$*":- 它将所有位置参数连接起来,形成一个单一的字符串。
- 各个参数之间用第一个字符(默认为空格)分隔。
- 你可以通过改变特殊变量
IFS(Internal Field Separator)来改变这个分隔符。例如,如果设置了IFS=,,那么"$*"的输出会变成one,two,three four。 - 只有一个循环迭代,因为所有参数被合并成了一个字符串。
-
"$@":- 它将每个位置参数都视为一个被引用的独立字符串。
- 这是最有用、最安全的用法。它保留了每个参数的原始边界。
- 参数
one、two和"three four"被完美地保留了。"three four"中的空格没有被拆分,它被正确地当作一个参数处理。 - 有三个循环迭代,对应我们传入的三个参数。
总结与使用建议
| 特性 | $* | $@ | 建议 |
|---|---|---|---|
| 不加引号 | 参数会经历单词拆分和文件名扩展 | 参数会经历单词拆分和文件名扩展 | 避免使用,行为不可预测 |
加引号 "..." | 一个字符串:将所有参数合并为一个字符串 | 参数列表:将每个参数作为独立的、被引用的字符串 | 这是正确的用法 |
| 何时使用 | 当你需要将所有参数作为一个整体处理时,例如,将它们全部打印在一行或用自定义分隔符连接它们。 | 几乎总是使用这个。当你需要将参数原封不动地传递给另一个命令,或者需要循环处理每个参数时。 | 优先使用 "$@" |
黄金法则:
几乎在所有情况下,你都应该使用加引号的
"$@"来访问脚本的位置参数。 这是保留参数原始含义和数量的最安全、最可靠的方法。
例如,编写一个包装脚本时,你需要将收到的所有参数都传递给另一个命令:
#!/bin/bash
# wrapper.sh
# 将收到的所有参数原封不动地传递给 `git`
# 使用 "$@" 是唯一正确的方法
git "$@"
运行 bash wrapper.sh commit -m "my message","$@" 能确保 -m 和 "my message" 作为两个独立的参数传递给 git,而如果使用 "$*",整个命令会变成 git commit -m my message,这完全是错误的。

222

被折叠的 条评论
为什么被折叠?



