在Bash完成的上下文中,有关$ {array }与$ {array }的混淆
我第一次试图写一个Bash完成,而我对有两种解引用Bash数组( ${array[@]}
和${array[*]}
)的方法有些困惑。
下面是相关的代码块(顺便说一句,它可以工作,但我想更好地理解它):
_switch() { local cur perls local ROOT=${PERLBREW_ROOT:-$HOME/perl5/perlbrew} COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} perls=($ROOT/perls/perl-*) # remove all but the final part of the name perls=(${perls[*]##*/}) COMPREPLY=( $( compgen -W "${perls[*]} /usr/bin/perl" -- ${cur} ) ) }
Bash的文档说 :
数组中的任何元素都可以使用$ {name [subscript]}引用。 大括号是为了避免与shell的文件名扩展操作符冲突。 如果下标是“@”或“*”,则该单词将扩展为数组名称的所有成员。 这些下标仅在双引号出现在单引号内时才有所不同。 如果单词是双引号,则$ {name [*]}将扩展为单个单词,每个数组成员的值由IFSvariables的第一个字符分隔,$ {name [@]}扩展每个名称元素到一个单独的词。
现在我想我明白了, compgen -W
期望一个包含可能替代compgen -W
的string,但在这种情况下,我不明白“$ {name [@]}将每个名称元素扩展为单独的单词”的意思。
长话短说: ${array[*]}
起作用; ${array[@]}
不。 我想知道为什么,我想更好地了解${array[@]}
究竟扩展到什么地方。
(这是对我对Kaleb Pederson的回答的评论的扩展 – 请参阅[@]
和[*]
的更一般处理的答案[*]
。
当bash(或任何类似的shell)parsing一个命令行时,它会将它分成一系列“单词”(我将称之为“shell-words”,以避免混淆)。 通常,shell-words是由空格(或其他空格)分隔的,但是空格可以通过转义或引用来包含在shell-word中。 双引号中的[@]
和[*]
扩展数组之间的区别在于"${myarray[@]}"
导致数组中的每个元素被当作一个单独的shell单词处理,而"${myarray[*]}"
结果是一个单独的shell字,数组中的所有元素用空格分隔(或者是IFS
的第一个字符)。
通常, [@]
行为就是你想要的。 假设我们有perls=(perl-one perl-two)
并使用ls "${perls[*]}"
– 这相当于ls "perl-one perl-two"
,它将查找单个名为perl-one perl-two
文件perl-one perl-two
,这可能不是你想要的。 ls "${perls[@]}"
相当于ls "perl-one" "perl-two"
,这更有可能做一些有用的事情。
提供完成单词列表(我将称之为comp-words,以避免与shell-words混淆) compgen
是不同的; -W
选项包含一个comp-words的列表,但它必须是一个单个shell-word的forms,而comp-words用空格分隔。 请注意,总是带有参数的命令选项(至less据我所知)采用一个单一的shell-word,否则无法知道选项的参数何时结束,而常规命令参数(/ other选项标志)开始。
更详细地说:
perls=(perl-one perl-two) compgen -W "${perls[*]} /usr/bin/perl" -- ${cur}
相当于:
compgen -W "perl-one perl-two /usr/bin/perl" -- ${cur}
…这是你想要的。 另一方面,
perls=(perl-one perl-two) compgen -W "${perls[@]} /usr/bin/perl" -- ${cur}
相当于:
compgen -W "perl-one" "perl-two /usr/bin/perl" -- ${cur}
…这是完全废话:“perl-one”是附加到-W标志的唯一comp-word,第一个真正的参数 – compgen将作为string完成 – 是“perl-two在/ usr / bin中/ perl的”。 我希望compgen抱怨说它已经被赋予了额外的参数(“ – ”和$ cur中的任何内容),但显然它只是忽略了它们。
你的标题询问${array[@]}
与${array[*]}
但是你问$array[*]
与$array[@]
有点混淆。 我会回答两个问题:
引用数组variables并使用@
作为下标时,数组中的每个元素都会扩展为其全部内容,而不pipe其内容是否存在可能存在的空白(实际上是$ IFS之一)。 当你使用星号( *
)作为下标时(无论是否引用),它可能扩展到通过在$ IFS中分解每个数组元素的内容而创build的新内容。
以下是示例脚本:
#!/bin/sh myarray[0]="one" myarray[1]="two" myarray[3]="three four" echo "with quotes around myarray[*]" for x in "${myarray[*]}"; do echo "ARG[*]: '$x'" done echo "with quotes around myarray[@]" for x in "${myarray[@]}"; do echo "ARG[@]: '$x'" done echo "without quotes around myarray[*]" for x in ${myarray[*]}; do echo "ARG[*]: '$x'" done echo "without quotes around myarray[@]" for x in ${myarray[@]}; do echo "ARG[@]: '$x'" done
这是它的输出:
with quotes around myarray[*] ARG[*]: 'one two three four' with quotes around myarray[@] ARG[@]: 'one' ARG[@]: 'two' ARG[@]: 'three four' without quotes around myarray[*] ARG[*]: 'one' ARG[*]: 'two' ARG[*]: 'three' ARG[*]: 'four' without quotes around myarray[@] ARG[@]: 'one' ARG[@]: 'two' ARG[@]: 'three' ARG[@]: 'four'
我个人通常希望“$ {myarray [@]}”。 现在,回答你的问题的第二部分, ${array[@]}
与$array[@]
。
引用你引用的bash文档:
大括号是为了避免与shell的文件名扩展操作符冲突。
$ myarray= $ myarray[0]="one" $ myarray[1]="two" $ echo ${myarray[@]} one two
但是,当你做$myarray[@]
,美元符号紧紧地绑定在myarray上,所以它在[@]
之前被评估。 例如:
$ ls $myarray[@] ls: cannot access one[@]: No such file or directory
但是,正如在文档中指出的,括号是文件名扩展,所以让我们试试这个:
$ touch one@ $ ls $myarray[@] one@
现在我们可以看到文件扩展发生在$myarray
扩展之后。
还有一点需要注意的是,没有下标的$myarray
展开为数组的第一个值:
$ myarray[0]="one four" $ echo $myarray[5] one four[5]