访问 bash 命令行参数 $@ vs $*

Posted

技术标签:

【中文标题】访问 bash 命令行参数 $@ vs $*【英文标题】:Accessing bash command line args $@ vs $* 【发布时间】:2012-09-01 02:55:08 【问题描述】:

在许多 SO 问题和 bash 教程中,我看到我可以通过两种方式访问​​ bash 脚本中的命令行参数:

$ ~ >cat testargs.sh 
#!/bin/bash

echo "you passed me" $*
echo "you passed me" $@

结果:

$ ~> bash testargs.sh arg1 arg2
you passed me arg1 arg2
you passed me arg1 arg2

$*$@ 有什么区别? 什么时候用前者,什么时候用后者?

【问题讨论】:

看看这个答案:***.com/a/842325/671366 IntelliJ 中的静态分析将echo "something $@" 视为错误 【参考方案1】:

引用特殊参数时会出现差异。让我来说明差异:

$ set -- "arg  1" "arg  2" "arg  3"

$ for word in $*; do echo "$word"; done
arg
1
arg
2
arg
3

$ for word in $@; do echo "$word"; done
arg
1
arg
2
arg
3

$ for word in "$*"; do echo "$word"; done
arg  1 arg  2 arg  3

$ for word in "$@"; do echo "$word"; done
arg  1
arg  2
arg  3

另一个关于引用重要性的例子:注意“arg”和数字之间有2个空格,但如果我没有引用$word:

$ for word in "$@"; do echo $word; done
arg 1
arg 2
arg 3

在 bash 中,"$@" 是要迭代的“默认”列表:

$ for word; do echo "$word"; done
arg  1
arg  2
arg  3

【讨论】:

是否有可能的用例,当$*"$*" 可能需要,而$@"$@" 无法达到目的时? 哪个版本更适合“包装”脚本,其中脚本参数需要成为新命令的参数? @Segfault,在这种情况下,请始终选择带引号的"$@" 这个答案包含有用的例子,但如果它也解释了它们背后的机制会更好。 为什么会这样工作? 代替for word ...循环,你可以写:printf "%s\n" "$*"printf "%s\n" "$@"printf "%s\n" $@进行比较! (... 甚至printf %s\\n "$@" ;)【参考方案2】:

来自Bash Hackers Wiki 的方便的概览表:

Syntax Effective result
$* $1 $2 $3 … $N
$@ $1 $2 $3 … $N
"$*" "$1c$2c$3c…c$N"
"$@" "$1" "$2" "$3" … "$N"

其中第三行中的c$IFS 的第一个字符,Input Field Separator,一个shell 变量。

如果参数要存储在脚本变量中并且参数应该包含空格,我衷心推荐使用"$*" trick with the input field separator set to tab IFS=$'\t'

【讨论】:

这里是an example,其中包括引用的输入。输入也很重要! 假设我想创建一个包装脚本,它除了模仿包装命令的功能之外什么都不做。我应该使用哪种语法将 args 从包装脚本传递到内部命令? @MarinosAn 使用"$@"(带引号)。其他变体都不起作用【参考方案3】:

$*

从一开始扩展到位置参数。当。。。的时候 扩展发生在双引号内,它扩展为单个单词 每个参数的值由第一个字符分隔 IFS 特殊变量。即“$*”等价于“$1c$2c...”, 其中 c 是 IFS 变量值的第一个字符。如果 IFS 未设置,参数以空格分隔。如果 IFS 为空, 参数在没有中间分隔符的情况下连接。

$@

从一开始扩展到位置参数。当。。。的时候 扩展发生在双引号内,每个参数扩展为 单独的词。也就是说,"$@" 等价于 "$1" "$2" ...如果 双引号扩展出现在一个单词中,扩展 第一个参数与原始的开始部分连接 词,最后一个参数的扩展与最后一个连接 原词的一部分。当没有位置参数时, "$@" 和 $@ 扩展为空(即,它们被删除)。

来源:Bash man

【讨论】:

【参考方案4】:

$@ 和 $* 一样,但是每个参数都是一个带引号的字符串,即参数原封不动地传递,没有解释或扩展。这意味着参数列表中的每个参数都被视为一个单独的词。

当然,“$@”应该加引号。

http://tldp.org/LDP/abs/html/internalvariables.html#ARGLIST

【讨论】:

【参考方案5】:

这个例子让我们在使用“at”和“asterix”时强调它们之间的区别。 我声明了两个数组“fruits”和“vegetables”

fruits=(apple pear plumm peach melon)            
vegetables=(carrot tomato cucumber potatoe onion)

printf "Fruits:\t%s\n" "$fruits[*]"            
printf "Fruits:\t%s\n" "$fruits[@]"            
echo + --------------------------------------------- +      
printf "Vegetables:\t%s\n" "$vegetables[*]"    
printf "Vegetables:\t%s\n" "$vegetables[@]"    

查看上面代码的结果如下:

Fruits: apple pear plumm peach melon
Fruits: apple
Fruits: pear
Fruits: plumm
Fruits: peach
Fruits: melon
+ --------------------------------------------- +
Vegetables: carrot tomato cucumber potatoe onion
Vegetables: carrot
Vegetables: tomato
Vegetables: cucumber
Vegetables: potatoe
Vegetables: onion

【讨论】:

从科学上讲,西红柿是水果。 你说得对! “在植物学中,果实是开花植物(也称为被子植物)在开花后由子房形成的含种子结构。” en.wikipedia.org/wiki/Fruit @Randy:科学地说,所有的水果都是蔬菜(它是“植物”的代名词)。 @CrisLuengo 异端! :)

以上是关于访问 bash 命令行参数 $@ vs $*的主要内容,如果未能解决你的问题,请参考以下文章

如何访问函数内调用者的命令行参数?

在 linux/bash 中添加到命令行参数

如何在 Bash 中更改命令行参数?

vs2010 怎么给c++项目预设命令行参数

Bash 脚本无法识别命令行参数? [复制]

Bash Shell中命令行选项/参数处理