Tel*_*hus 73 arrays bash bash-completion
我正在尝试第一次编写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} ) )
}
Run Code Online (Sandbox Code Playgroud)
Bash的文档说:
可以使用$ {name [subscript]}引用数组的任何元素.需要大括号以避免与shell的文件名扩展运算符冲突.如果下标是'@'或'*',则该单词将扩展为数组名称的所有成员.这些下标仅在单词出现在双引号内时有所不同.如果单词是双引号,则$ {name [*]}扩展为单个单词,每个数组成员的值由IFS变量的第一个字符分隔,$ {name [@]}扩展名称的每个元素一个单独的词.
现在我想我明白,compgen -W期望一个字符串包含可能的替代词列表,但在这种情况下,我不明白"$ {name [@]}将名称的每个元素扩展为单独的词"是指.
长话短说:${array[*]}作品; ${array[@]}没有.我想知道为什么,我想更好地理解究竟是什么${array[@]}扩展到了什么.
Gor*_*son 93
(这是我在卡莱布佩德森的回答评论的扩展-看到答案的更一般的治疗[@]VS [*]).
当bash(或任何类似的壳)解析命令行,它分裂成一系列"字"(我将称之为"壳词语"后,以避免混淆).通常,壳字由空格(或其它空格)通过转义或引用它们分离,但可被包括在空间的壳字.之间的差[@]和[*]在双引号-expanded阵列是"${myarray[@]}"通向数组的每个元素被视为一个单独的壳字,而"${myarray[*]}"结果在单个壳字与所有由空格分隔阵列的元件(或无论第一个角色IFS是什么).
通常,[@]行为就是你想要的.假设我们拥有perls=(perl-one perl-two)并使用ls "${perls[*]}"- 这相当于ls "perl-one perl-two",它将寻找命名的单个文件perl-one perl-two,这可能不是你想要的. ls "${perls[@]}"相当于ls "perl-one" "perl-two",更有可能做一些有用的事情.
提供完成单词列表(我将其称为comp-words以避免与shell-words混淆)compgen是不同的; 该-W选项采用复合词列表,但它必须采用单个shell-word的形式,其中comp-words由空格分隔.请注意,始终带参数的命令选项(至少就我所知)采用单个shell-word - 否则无法判断选项的参数何时结束,以及常规命令参数(/ other)选项标志)开始.
更详细:
perls=(perl-one perl-two)
compgen -W "${perls[*]} /usr/bin/perl" -- ${cur}
Run Code Online (Sandbox Code Playgroud)
相当于:
compgen -W "perl-one perl-two /usr/bin/perl" -- ${cur}
Run Code Online (Sandbox Code Playgroud)
......做你想做的事.另一方面,
perls=(perl-one perl-two)
compgen -W "${perls[@]} /usr/bin/perl" -- ${cur}
Run Code Online (Sandbox Code Playgroud)
相当于:
compgen -W "perl-one" "perl-two /usr/bin/perl" -- ${cur}
Run Code Online (Sandbox Code Playgroud)
...这完全是废话:"perl-one"是唯一附加到-W标志的comp-word,第一个真正的参数 - compgen将作为要完成的字符串 - 是"perl-two"在/ usr/bin中/ perl的".我希望compgen抱怨它已被给予额外的参数(" - "以及$ cur中的任何内容),但显然它只是忽略了它们.
Kal*_*son 49
你的标题问起${array[@]}对${array[*]},但随后你问$array[*]与$array[@]这是一个有点混乱.我会回答两个:
当引用数组变量并@用作下标时,数组的每个元素都会扩展为其完整内容,而不管该内容$IFS中可能存在的空格(实际上是其中之一).当您使用星号(*)作为下标(无论是否引用它)时,它可能会扩展为通过分解每个数组元素的内容而创建的新内容$IFS.
这是示例脚本:
#!/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
Run Code Online (Sandbox Code Playgroud)
这是它的输出:
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'
Run Code Online (Sandbox Code Playgroud)
我个人通常想要的"${myarray[@]}".现在,回答你问题的第二部分,${array[@]}对比$array[@].
引用你引用的bash文档:
需要大括号以避免与shell的文件名扩展运算符冲突.
$ myarray=
$ myarray[0]="one"
$ myarray[1]="two"
$ echo ${myarray[@]}
one two
Run Code Online (Sandbox Code Playgroud)
但是,当你这样做时$myarray[@],美元符号紧紧地绑定在一起,myarray因此它在之前被评估[@].例如:
$ ls $myarray[@]
ls: cannot access one[@]: No such file or directory
Run Code Online (Sandbox Code Playgroud)
但是,如文档中所述,括号用于文件名扩展,所以让我们试试这个:
$ touch one@
$ ls $myarray[@]
one@
Run Code Online (Sandbox Code Playgroud)
现在我们可以看到扩展后发生了文件名扩展$myarray.
还有一个注意事项,$myarray没有下标扩展到数组的第一个值:
$ myarray[0]="one four"
$ echo $myarray[5]
one four[5]
Run Code Online (Sandbox Code Playgroud)