的用法!在参数扩展中

的用法!在参数扩展中

https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html

参数展开的基本形式是${parameter}。 ...

如果参数的第一个字符是感叹号 ( !),则引入一级变量间接寻址。 Bash 使用由参数的其余部分形成的变量的值作为变量的名称;然后扩展该变量,并在其余替换中使用该值,而不是参数本身的值。这称为间接扩展。例外情况是下面描述的${!prefix}扩展。${!name[@]}感叹号必须紧跟在左大括号之后才能引入间接关系。

...

${!prefix*} ${!prefix@}扩展为名称以 prefix 开头的变量名称,由 IFS 特殊变量的第一个字符分隔。当使用“@”并且扩展出现在双引号内时,每个变量名称都会扩展为一个单独的单词。

${!name[@]} ${!name[*]}如果 name 是数组变量,则扩展到 name 中分配的数组索引(键)列表。如果 name 不是数组,则在设置 name 时扩展为 0,否则扩展为 null。当使用“@”并且扩展出现在双引号内时,每个键都会扩展为一个单独的单词。

您能为引用的段落举一些例子吗?我不知道他们的意思。

答案1

我们需要比较(并区分):

"${Var}"          # Plain variable
"${!Var}"         # Indirect expansion
"${!Var@}"        # Prefix expansion
"${!Var[@]}"      # Array keys expansion
"${Var[@]}"       # Plain array expansion

还有一些*非常相似但有细微差别的扩展。

间接

间接示例:

$ varname=var_one
$ var_one=a-value

$ echo "${varname}"
var_one
$ echo "${!varname} and ${var_one}"
a-value and a-value

字首

前缀示例:

$ head_one=foo
$ head_two=bar

$ printf '<%s> ' "${!head@}"
<head_one> <head_two>
$ printf '<%s> ' "${!head*}"
<head_one head_two>

请注意,变量通过 IFS 的第一个字符粘合在一起,默认情况下该字符是空格(就像 IFSSpace Tab NewLine默认情况下一样)。


普通阵列

数组示例(未使用!)显示@和的微小(但重要)差异*

$ Array[1]=This
$ Array[2]=is
$ Array[3]=a
$ Array[4]=simple
$ Array[5]=test.

$ printf '<%s> ' "${Array[@]}"
<This> <is> <a> <simple> <test.>

$ printf '<%s> ' "${Array[*]}"
<This is a simple test.>

关于 IFS 的相同评论也适用于此。

请注意,我没有(故意)分配 Array 的索引 0。

请注意,分配数组的更简单方法是:

$ Array=( "" This is a simple test.)

但这里必须使用索引0,而我使用了一个空值(这与上面未设置的值不同)。


数组列表

为此,一个简单的索引数组(带有数字)就没那么有趣了:

$ Array=( "" A simple example of an array.)
$ printf '<%s> ' "${!Array[@]}"
<0> <1> <2> <3> <4> <5> <6> 
$ printf '<%s> ' "${!Array[*]}"
<0 1 2 3 4 5 6>

但对于关联数组,事情变得更有趣

$ unset Array                               # erase any notion of variable array.
$ declare -A Array                          # make it associative

$ Array=([foo]=one [bar]=two [baz]=three)   # give it values.

$ printf '<%s> ' "${Array[@]}"
<two> <three> <one>                         # List of values.

$ printf '<%s> ' "${!Array[@]}"
<bar> <baz> <foo>                           # List of keys

$ printf '<%s> ' "${Array[*]}"
<two three one>                             # One string of list of values.

$ printf '<%s> ' "${!Array[*]}"
<bar baz foo>                               # One string of list of keys.

请注意,顺序与分配时不同。


笔记:我提出的所有用法都被引用"${!Array[@]}",未引用的值${!Array[@]}${!Array[*]}工作完全相同,给出相同的输出(在 Bash 中)。
但会受到 shell 分裂对 IFS 值的影响。还有丑陋的、总是有问题的“路径名扩展”。一般来说不太有用。或者无论如何都要非常小心地使用。

答案2

看一眼BinaryZebra 的回答以获得详细的解释。以下来自 TLDP 的引用据说是不正确的。

原来的:

看着http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html:

如果“PARAMETER”的第一个字符是感叹号,Bash 使用“PARAMETER”其余部分形成的变量值作为变量的名称;然后扩展该变量,并在其余替换中使用该值,而不是“PARAMETER”本身的值。这称为间接扩展。

您当然熟悉直接参数扩展,因为它一直在发生,即使是在最简单的情况下,例如上面或下面的情况:

franky ~> echo $SHELL
/bin/bash

下面是间接扩展的例子:

franky ~> echo ${!N*}
NNTPPORT NNTPSERVER NPX_PLUGIN_PATH

请注意,这与 echo 不同$N*

您可以在 shell 中看到它的效果。

例子:

$ TEST=(test1 test2 test3)
$ echo ${!TEST*}
TEST
$ echo ${!TEST[@]}
0 1 2
$ echo ${TEST[@]}
test1 test2 test3
$ echo ${#TEST[@]}
4

${!TEST[@]}请注意使用、${TEST[@]}和之间的区别${#TEST[@]}

相关内容