$ @变量似乎保持引用它的参数,例如:
$ function foo { for i in "$@"; do echo $i; done }
$ foo herp "hello world" derp
herp
hello world
derp
我也知道bash数组的工作方式相同:
$ a=(herp "hello world" derp)
$ for i in "${a[@]}"; do echo $i; done
herp
hello world
derp
这样的变量究竟发生了什么?特别是当我在报价中添加一些内容,比如“duck $ {a [@]} goose”。如果它的空间不分隔是什么?
答案 0 :(得分:11)
通常,Bash中的双引号表示“将引号之间的所有内容都设为一个单词,即使它中包含分隔符。”但正如您所注意到的,$@
在双引号内表现不同。这实际上是一个可以追溯到Bash的前身Bourne shell的解析hack,这种特殊行为仅适用于这个特定的变量。
没有这个hack(我使用这个术语,因为它从语言的角度来看似乎不一致,虽然它非常有用),但是shell脚本很难将其参数数组传递给需要相同参数的其他命令。其中一些参数可能在其中有空格,但是如果没有shell将它们作为一个大词汇集在一起或者重新分析列表并拆分具有空格的参数,它将如何将它们传递给另一个命令?
好吧,你可以传递一个参数数组,而Bourne shell实际上只有一个数组,由$*
或$@
表示,其元素数量为$#
及其元素是$1
,$2
等,即所谓的位置参数。
示例。假设当前目录中有三个文件,名为aaa
,bbb
和cc c
(第三个文件中有空格)名字)。您可以将数组初始化(即,您可以设置位置参数)作为当前目录中文件的名称,如下所示:
set -- *
现在,位置参数数组包含文件的名称。 $#
,元素数量为三:
$ echo $#
3
我们可以通过几种不同的方式迭代位置参数。
1)我们可以使用$*
:
$ for file in $*; do
> echo "$file"
> done
但是重新分隔空格上的参数并调用echo四次:
aaa
bbb
cc
c
2)或者我们可以在$*
附近加上引号:
$ for file in "$*"; do
> echo "$file"
> done
但是它将整个数组分组为一个参数并且只调用一次echo:
aaa bbb cc c
3)或者我们可以使用代表相同数组的$@
但在双引号中表现不同:
$ for file in "$@"; do
> echo "$file"
> done
将产生
aaa
bbb
cc c
因为$ 1 =“aaa”,$ 2 =“bbb”,$ 3 =“cc c”和"$@"
使元素保持不变。如果你在$@
周围留下引号,那么shell会变平并重新解析数组,echo会被调用四次,你将得到与裸$*
相同的东西。
这在shell脚本中特别有用,其中位置参数是传递给脚本的参数。要将这些相同的参数传递给其他命令 - 如果没有shell在空格上重新分割它们 - 请使用"$@"
。
# Truncate the files specified by the args
rm "$@"
touch "$@"
在Bourne中,此行为仅适用于位置参数,因为它实际上是该语言支持的唯一数组。但是你可以在 Bash 中创建其他数组,你甚至可以使用特殊的"${ARRAYNAME[@]}"
语法将旧的解析hack应用于那些数组,其符号感觉几乎就像是先生的眨眼。伯恩:
$ declare -a myarray
$ myarray[0]=alpha
$ myarray[1]=bravo
$ myarray[2]="char lie"
$ for file in "${myarray[@]}"; do echo "$file"; done
alpha
bravo
char lie
哦,关于你的最后一个例子,shell应该用"pre $@ post"
做什么,你在{2}内有$@
但你还有其他的东西?最新版本的Bash保留数组,将$@
之前的文本添加到第一个数组元素之前,并将$@
之后的文本追加到最后一个元素:
pre aaa
bb
cc c post