在 bash 完成的上下文中关于 ${array[*]} 与 ${array[@]} 的混淆 [英] A confusion about ${array[*]} versus ${array[@]} in the context of a bash completion

查看:31
本文介绍了在 bash 完成的上下文中关于 ${array[*]} 与 ${array[@]} 的混淆的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我第一次尝试编写 bash 补全,我对取消引用 bash 数组的两种方法有点困惑 (${array[@]}${array[*]}).

I'm taking a stab at writing a bash completion for the first time, and I'm a bit confused about about the two ways of dereferencing bash arrays (${array[@]} and ${array[*]}).

这是相关的代码块(顺便说一下,它可以工作,但我想更好地理解它):

Here's the relevant chunk of code (it works, by the way, but I would like to understand it better):

_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 的每个元素到一个单独的词.

Any element of an array may be referenced using ${name[subscript]}. The braces are required to avoid conflicts with the shell's filename expansion operators. If the subscript is ‘@’ or ‘*’, the word expands to all members of the array name. These subscripts differ only when the word appears within double quotes. If the word is double-quoted, ${name[*]} expands to a single word with the value of each array member separated by the first character of the IFS variable, and ${name[@]} expands each element of name to a separate word.

现在我想我明白 compgen -W 需要一个包含可能替代词列表的字符串,但在这种情况下我不明白${name[@]} 扩展每个元素是什么名称到一个单独的词"的意思.

Now I think I understand that compgen -W expects a string containing a wordlist of possible alternatives, but in this context I don't understand what "${name[@]} expands each element of name to a separate word" means.

长话短说:${array[*]} 有效;${array[@]} 没有.我想知道为什么,我想更好地理解 ${array[@]} 扩展成什么.

Long story short: ${array[*]} works; ${array[@]} doesn't. I would like to know why, and I would like to understand better what exactly ${array[@]} expands into.

推荐答案

(这是我对 Kaleb Pederson 答案的评论的扩展——请参阅该答案以了解 [@]vs [*].)

(This is an expansion of my comment on Kaleb Pederson's answer -- see that answer for a more general treatment of [@] vs [*].)

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

When bash (or any similar shell) parses a command line, it splits it into a series of "words" (which I will call "shell-words" to avoid confusion later). Generally, shell-words are separated by spaces (or other whitespace), but spaces can be included in a shell-word by escaping or quoting them. The difference between [@] and [*]-expanded arrays in double-quotes is that "${myarray[@]}" leads to each element of the array being treated as a separate shell-word, while "${myarray[*]}" results in a single shell-word with all of the elements of the array separated by spaces (or whatever the first character of IFS is).

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

Usually, the [@] behavior is what you want. Suppose we have perls=(perl-one perl-two) and use ls "${perls[*]}" -- that's equivalent to ls "perl-one perl-two", which will look for single file named perl-one perl-two, which is probably not what you wanted. ls "${perls[@]}" is equivalent to ls "perl-one" "perl-two", which is much more likely to do something useful.

compgen 提供完成词列表(我将其称为 comp-words 以避免与 shell-words 混淆)是不同的;-W 选项需要一个合成词列表,但它必须是单个 shell-word 的形式,合成词用空格分隔.请注意,采用参数的命令选项始终(至少据我所知)采用单个 shell 字——否则将无法判断选项的参数何时结束,以及常规命令参数(/other选项标志)开始.

Providing a list of completion words (which I will call comp-words to avoid confusion with shell-words) to compgen is different; the -W option takes a list of comp-words, but it must be in the form of a single shell-word with the comp-words separated by spaces. Note that command options that take arguments always (at least as far as I know) take a single shell-word -- otherwise there'd be no way to tell when the arguments to the option end, and the regular command arguments (/other option flags) begin.

更详细:

perls=(perl-one perl-two)
compgen -W "${perls[*]} /usr/bin/perl" -- ${cur}

相当于:

compgen -W "perl-one perl-two /usr/bin/perl" -- ${cur}

...它可以满足您的需求.另一方面,

...which does what you want. On the other hand,

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-两个/usr/bin/perl".我希望 compgen 会抱怨它被赋予了额外的参数(--"以及 $cur 中的任何内容),但显然它只是忽略了它们.

...which is complete nonsense: "perl-one" is the only comp-word attached to the -W flag, and the first real argument -- which compgen will take as the string to be completed -- is "perl-two /usr/bin/perl". I'd expect compgen to complain that it's been given extra arguments ("--" and whatever's in $cur), but apparently it just ignores them.

这篇关于在 bash 完成的上下文中关于 ${array[*]} 与 ${array[@]} 的混淆的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆