为什么$ @与bash中的大多数其他变量不同?

时间:2011-06-10 18:39:58

标签: bash quotes

$ @变量似乎保持引用它的参数,例如:

$ 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”。如果它的空间不分隔是什么?

1 个答案:

答案 0 :(得分:11)

通常,Bash中的双引号表示“将引号之间的所有内容都设为一个单词,即使它中包含分隔符。”但正如您所注意到的,$@在双引号内表现不同。这实际上是一个可以追溯到Bash的前身Bourne shell的解析hack,这种特殊行为仅适用于这个特定的变量。

没有这个hack(我使用这个术语,因为它从语言的角度来看似乎不一致,虽然它非常有用),但是shell脚本很难将其参数数组传递给需要相同参数的其他命令。其中一些参数可能在其中有空格,但是如果没有shell将它们作为一个大词汇集在一起​​或者重新分析列表并拆分具有空格的参数,它将如何将它们传递给另一个命令?

好吧,你可以传递一个参数数组,而Bourne shell实际上只有一个数组,由$*$@表示,其元素数量为$#及其元素是$1$2等,即所谓的位置参数。

示例。假设当前目录中有三个文件,名为aaabbbcc 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