在bash完成的背景下,关于$ {array [*]}与$ {array [@]}的混淆


87

我第一次尝试编写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 [*]}扩展为单个单词,每个数组成员的值均由IFS变量的第一个字符分隔,而$ {name [@]}扩展每个name元素换一个词。

现在,我想我知道应该compgen -W期望一个包含可能的替代词列表的字符串,但是在这种情况下,我不理解“ $ {name [@]}将名称的每个元素扩展为一个单独的词”的含义。

长话短说:${array[*]}作品;${array[@]}没有。我想知道为什么,并且我想更好地理解将要${array[@]}扩展的内容。

Answers:


116

(这是我对Kaleb Pederson的回答的评论的扩展-有关[@]vs的更一般性处理,请参见该回答[*]。)

当bash(或任何类似的shell)解析命令行时,它会将其拆分为一系列“单词”(我将其称为“ shell-words”,以避免以后产生混淆)。通常,shell词由空格(或其他空白)分隔,但是可以通过转义或引用空格来将空格包含在shell词中。[@]和-[*]扩展数组中双引号之间的区别是,"${myarray[@]}"导致将数组的每个元素都视为一个单独的shell字,而"${myarray[*]}"导致单个shell字,其中数组的所有元素都由空格(或不管第一个字符IFS是什么)。

通常,[@]行为就是您想要的。假设我们拥有perls=(perl-one perl-two)并使用ls "${perls[*]}"-等效于ls "perl-one perl-two",它将查找名为的单个文件perl-one perl-two,这可能不是您想要的。 ls "${perls[@]}"等价于ls "perl-one" "perl-two",这很有可能会做一些有用的事情。

提供的补全单词列表(为了避免与shell单词混淆,我将其称为comp-words)compgen;该-W选项采用comp-word列表,但是必须采用单个shell-word的形式,且comp-word以空格分隔。请注意,带有参数的命令选项始终(至少就我所知)使用单个shell字-否则,将无法分辨该选项的参数何时结束,而常规命令参数(/ 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将作为要完成的字符串)是“ perl-two” / usr / bin / perl”。我希望compgen抱怨它被赋予了额外的参数(“-”以及$ cur中的任何内容),但显然它只是忽略了它们。


3
这太好了;谢谢。我真的希望它能大声播放,但这至少可以弄清楚为什么它不起作用。
Telemachus

58

您的标题询问${array[@]}vs,${array[*]}但是随后询问$array[*]vs $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

这是输出:

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]

1
另见关于如何IFS将影响不同,这取决于输出@*和报价与不带引号的。
暂停,直到另行通知。

我很抱歉,因为在这种情况下这很重要,但我总是指${array[*]}${array[@]}。缺少括号只是粗心大意。除此之外,您能解释一下${array[*]}compgen命令中的内容吗?也就是说,在这种情况下,将数组分别扩展为其每个元素意味着什么?
Telemachus

换句话说,您(几乎像每个来源一样)都说这${array[@]}是通常的方法。我想了解的是为什么在这种情况下只能 ${array[*]}起作用。
Telemachus

1
这是因为-W选项随附的单词表必须作为一个单词给出(然后compgen然后根据IFS进行拆分)。如果在递给compgen之前将其拆分成单独的单词([@]就是这样),compgen会认为只有第一个与-W一起使用,其余都是常规参数(而且我认为它只希望有一个参数,因此会barf)。
戈登·戴维森

@戈登:将其移至答案,我会接受的。这就是我真正想知道的。谢谢。(顺便说一句,它不会以明显的方式吠叫。它会默默地吠叫-很难知道出了什么问题。)
Telemachus 2010年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.