Shell 脚本中 $@ 与 $* 参数扩展有什么区别?

文章导读
在绝大多数场景下,传递参数时请优先使用双引号包裹的 "$@",它能确保每个参数边界独立,避免含空格的参数被错误拆分。
📋 目录
  1. 命令速用版
  2. 为什么会这样
  3. 分步处理
  4. 怎么验证是否生效
  5. 常见坑
  6. 参考来源
A A

在绝大多数场景下,传递参数时请优先使用双引号包裹的 "$@",它能确保每个参数边界独立,避免含空格的参数被错误拆分。

先说结论:"$@" 是安全传递参数的标准做法,"$*" 仅在明确需要合并所有参数为单个字符串时使用。

  • 适合:需要原样传递脚本或函数的所有位置参数时,尤其是参数中可能包含空格。
  • 重点看:变量是否被双引号包裹,这是行为差异的关键,不加引号时两者表现基本一致。
  • 别忽略:for 循环中遍历参数时,误用 $* 会导致含空格的参数被截断。

命令速用版

如果你需要快速验证区别,可以直接运行以下单行命令观察效果:

bash -c 'for i in "$*"; do echo "[$i]"; done' _ "arg 1" "arg 2"
bash -c 'for i in "$@"; do echo "[$i]"; done' _ "arg 1" "arg 2"

第一行会输出一个合并的字符串,第二行会输出两个独立的参数。

为什么会这样

这两个变量都代表脚本接收到的所有位置参数,但扩展规则不同。核心差异在于双引号内的处理方式:

"$*" 会将所有参数合并为一个单独的字符串,参数之间使用 IFS(内部字段分隔符,默认为空格)的第一个字符连接。这意味着原本独立的参数变成了一个大字符串。

"$@" 会将每个参数扩展为独立的字符串,相当于 "$1" "$2" ...。这保留了参数的原始边界,即使参数内部包含空格,也不会被拆分。

Shell 脚本中 $@ 与 $* 参数扩展有什么区别?

如果不加双引号,$*$@ 都会经历单词拆分(word splitting),行为基本一致,都容易因空格导致参数断裂。

分步处理

编写脚本时,建议按照以下步骤处理参数传递:

1. 创建测试脚本
新建一个文件 test_args.sh,内容如下:

#!/bin/bash
echo "使用 \"$*\" 遍历:"
for arg in "$*"; do
    echo "  参数:[$arg]"
done

echo "使用 \"$@\" 遍历:"
for arg in "$@"; do
    echo "  参数:[$arg]"
done

2. 赋予执行权限
运行 chmod +x test_args.sh 确保脚本可执行。

3. 传入含空格的参数
执行脚本时,故意传入带空格的参数,例如:./test_args.sh "hello world" foo bar

怎么验证是否生效

观察上一步的输出结果:

Shell 脚本中 $@ 与 $* 参数扩展有什么区别?

如果使用 "$*",你会看到只有一行输出,内容是 [hello world foo bar],所有参数被合并了。

如果使用 "$@",你会看到三行输出,分别是 [hello world][foo][bar],参数边界被完整保留。

在正式脚本中,如果你需要将参数传递给另一个命令(如 cp "$@" /backup/),请确保输出符合预期,即每个文件名单独处理,不会因空格变成多个文件。

常见坑

1. 循环遍历参数
for arg in $*for arg in $@ 中如果不加双引号,参数中的空格会被当作分隔符,导致一个参数被拆成多个。务必写成 for arg in "$@"

2. 函数参数传递
在函数内部转发参数时,永远使用 func "$@"。如果使用 func "$*",接收端函数只会收到一个参数。

3. IFS 修改影响
"$*" 的连接符取决于 IFS 的第一个字符。如果脚本中修改过 IFS"$*" 的行为会随之改变,而 "$@" 不受影响,始终保留独立参数。

参考来源

  • GNU Bash Manual, Special Parameters, * and @ expansion rules. URL: https://www.gnu.org/software/bash/manual/html_node/Special-Parameters.html
  • Linux 社区技术文档关于 Shell 位置参数扩展的通用说明。