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

Jak*_*ake 5 bash quotes

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

$ function foo { for i in "$@"; do echo $i; done }
$ foo herp "hello world" derp
herp
hello world
derp
Run Code Online (Sandbox Code Playgroud)

我也知道bash数组,工作方式相同:

$ a=(herp "hello world" derp)
$ for i in "${a[@]}"; do echo $i; done
herp
hello world
derp
Run Code Online (Sandbox Code Playgroud)

这样的变量究竟发生了什么?特别是当我在报价中添加一些内容,比如"duck $ {a [@]} goose".如果它的空间不分隔是什么呢?

Rob*_*vis 11

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

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

嗯,你可以传递参数数组,和Bourne shell真的只有一个阵列,通过代表$*$@,其元素的数目是$#和其元素$1,$2等等,即所谓的位置参数.

一个例子.假设在当前目录下三个文件,命名aaa,bbbcc c(第三个文件的名称中有空格).您可以将数组初始化(即,您可以设置位置参数)作为当前目录中文件的名称,如下所示:

set -- *
Run Code Online (Sandbox Code Playgroud)

现在,位置参数数组包含文件的名称.$#,元素的数量,是三:

$ echo $#
3
Run Code Online (Sandbox Code Playgroud)

我们可以通过几种不同的方式迭代位置参数.


1)我们可以使用$*:

$ for file in $*; do
>    echo "$file"
> done
Run Code Online (Sandbox Code Playgroud)

但是重新分隔空格上的参数并调用echo四次:

aaa
bbb
cc
c
Run Code Online (Sandbox Code Playgroud)

2)或者我们可以在上面加上引号$*:

$ for file in "$*"; do
>    echo "$file"
> done
Run Code Online (Sandbox Code Playgroud)

但是它将整个数组分组为一个参数并且只调用一次echo:

aaa bbb cc c
Run Code Online (Sandbox Code Playgroud)

3)或者我们可以使用$@哪个表示相同的数组但在双引号中表现不同:

$ for file in "$@"; do
>     echo "$file"
> done
Run Code Online (Sandbox Code Playgroud)

会产生

aaa
bbb
cc c
Run Code Online (Sandbox Code Playgroud)

因为$ 1 ="aaa",$ 2 ="bbb",$ 3 ="cc c"并"$@"保持元素完好无损.如果你不引用引号$@,那么shell会变平并重新解析数组,echo会被调用四次,你会得到与裸露相同的东西$*.


这在shell脚本中特别有用,其中位置参数是传递给脚本的参数.要将这些相同的参数传递给其他命令 - 没有shell在空格上重新分割它们 - 请使用"$@".

# Truncate the files specified by the args
rm "$@"
touch "$@"
Run Code Online (Sandbox Code Playgroud)

在Bourne中,此行为仅适用于位置参数,因为它实际上是该语言支持的唯一数组.但是你可以在Bash中创建其他数组,你甚至可以使用特殊"${ARRAYNAME[@]}"语法将旧的解析hack应用于那些数组,其符号感觉几乎就像Bourne先生的眨眼:

$ 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
Run Code Online (Sandbox Code Playgroud)

哦,你的最后一个例子,什么都要壳做的"pre $@ post",你有$@双引号内,但你有其他的东西在里面,太?最新版本的Bash保留数组,$@在第一个数组元素之前添加文本,并将文本追加$@到最后一个元素之后:

pre aaa
bb
cc c post
Run Code Online (Sandbox Code Playgroud)