bash 完成上下文中 ${array[*]} 与 ${array[@]} 的混淆
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3348443/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
A confusion about ${array[*]} versus ${array[@]} in the context of a bash completion
提问by Telemachus
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[*]}
).
我第一次尝试编写 bash 完成,我对取消引用 bash 数组(${array[@]}
和${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's documentation says:
bash 的文档说:
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.
可以使用 ${name[subscript]} 引用数组的任何元素。需要大括号以避免与 shell 的文件名扩展运算符发生冲突。如果下标是 '@' 或 '*',则单词扩展为数组名称的所有成员。这些下标仅在单词出现在双引号内时才不同。如果单词是双引号,则 ${name[*]} 扩展为单个单词,每个数组成员的值由 IFS 变量的第一个字符分隔,${name[@]} 扩展 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.
现在我想我明白compgen -W
需要一个包含可能替代词列表的字符串,但在这种情况下,我不明白“${name[@]} 将 name 的每个元素扩展为一个单独的词”是什么意思。
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.
长话短说:${array[*]}
作品;${array[@]}
没有。我想知道为什么,我想更好地理解到底${array[@]}
扩展到什么。
回答by Gordon Davisson
(This is an expansion of my comment on Kaleb Pederson's answer -- see that answer for a more general treatment of [@]
vs [*]
.)
(这是我对 Kaleb Pederson 答案的评论的扩展——请参阅该答案以了解对[@]
vs的更一般处理[*]
。)
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).
当 bash(或任何类似的 shell)解析一个命令行时,它会将其拆分为一系列“单词”(我将其称为“shell-words”以避免以后混淆)。通常,shell-words 由空格(或其他空格)分隔,但空格可以通过转义或引用它们来包含在 shell-word 中。双引号中的[@]
和[*]
-expanded 数组之间的区别在于,导致数组的"${myarray[@]}"
每个元素都被视为一个单独的 shell-word,而"${myarray[*]}"
导致一个单独的 shell-word,数组的所有元素用空格(或无论 的第一个字符IFS
是什么)。
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.
通常,[@]
行为就是你想要的。假设我们有perls=(perl-one perl-two)
并使用ls "${perls[*]}"
- 这相当于ls "perl-one perl-two"
,它将查找名为 的单个文件perl-one perl-two
,这可能不是您想要的。 ls "${perls[@]}"
相当于ls "perl-one" "perl-two"
,这更有可能做一些有用的事情。
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.
提供完成词列表(我将其称为 comp-words 以避免与 shell-words 混淆)compgen
是不同的;该-W
选项需要一个合成词列表,但它必须是单个 shell-word 的形式,合成词用空格分隔。请注意,带参数的命令选项始终(至少据我所知)采用单个 shell 字——否则将无法判断选项的参数何时结束,以及常规命令参数(/other选项标志)开始。
In more detail:
更详细地:
perls=(perl-one perl-two)
compgen -W "${perls[*]} /usr/bin/perl" -- ${cur}
is equivalent to:
相当于:
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}
is equivalent to:
相当于:
compgen -W "perl-one" "perl-two /usr/bin/perl" -- ${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.
...这完全是胡说八道:“perl-one”是唯一附加到 -W 标志的 comp-word,第一个真正的参数——compgen 将作为要完成的字符串——是“perl-two /usr/bin/perl”。我希望 compgen 会抱怨它被赋予了额外的参数(“--”以及 $cur 中的任何内容),但显然它只是忽略了它们。
回答by Kaleb Pederson
Your title asks about ${array[@]}
versus ${array[*]}
but then you ask about $array[*]
versus $array[@]
which is a bit confusing. I'll answer both:
您的标题询问了${array[@]}
vs,${array[*]}
但随后您询问了$array[*]
vs $array[@]
,这有点令人困惑。我会回答两个:
When you quote an array variable and use @
as a subscript, each element of the array is expanded to its full content regardless of whitespace (actually, one of $IFS
) that may be present within that content. When you use the asterisk (*
) as the subscript (regardless of whether it's quoted or not) it may expand to new content created by breaking up each array element's content at $IFS
.
当您引用一个数组变量并@
用作下标时,数组的每个元素都会扩展到其完整内容,而不管该内容$IFS
中可能存在的空格(实际上是 之一)。当您使用星号 ( *
) 作为下标时(无论它是否被引用),它可能会扩展为通过在 处分解每个数组元素的内容而创建的新内容$IFS
。
Here's the example script:
这是示例脚本:
#!/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
And here's it's output:
这是它的输出:
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'
I personally usually want "${myarray[@]}"
. Now, to answer the second part of your question, ${array[@]}
versus $array[@]
.
我个人通常想要"${myarray[@]}"
。现在,回答您问题的第二部分,${array[@]}
而不是$array[@]
.
Quoting the bash docs, which you quoted:
引用您引用的 bash 文档:
The braces are required to avoid conflicts with the shell's filename expansion operators.
需要大括号以避免与 shell 的文件名扩展运算符发生冲突。
$ myarray=
$ myarray[0]="one"
$ myarray[1]="two"
$ echo ${myarray[@]}
one two
But, when you do $myarray[@]
, the dollar sign is tightly bound to myarray
so it is evaluated before the [@]
. For example:
但是,当您这样做时$myarray[@]
,美元符号myarray
与[@]
. 例如:
$ ls $myarray[@]
ls: cannot access one[@]: No such file or directory
But, as noted in the documentation, the brackets are for filename expansion, so let's try this:
但是,正如文档中所指出的,括号用于文件名扩展,所以让我们试试这个:
$ touch one@
$ ls $myarray[@]
one@
Now we can see that the filename expansion happened after the $myarray
exapansion.
现在我们可以看到文件名扩展发生在扩展之后$myarray
。
And one more note, $myarray
without a subscript expands to the first value of the array:
还有一点要注意,$myarray
没有下标会扩展到数组的第一个值:
$ myarray[0]="one four"
$ echo $myarray[5]
one four[5]