什么是间接扩张? ${!var*} 是什么意思?

发布于 2024-12-21 02:31:32 字数 595 浏览 4 评论 0原文

我正在阅读“Bash 初学者指南”。它说:

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

给出的例子是:

弗兰基~>回声 ${!N*}
NNTPPORT NNTPSERVER NPX_PLUGIN_PATH

这里我不太明白:

PARAMETER的其余部分形成的变量值

由于 PARAMETER 只是 !N*,那么

其余参数

只是 N*。这怎么能形成一个变量呢? Bash 是否在那里搜索了所有可能的命令?

I'm reading "Bash Guide for Beginners". It says:

If the first character of PARAMETER is an exclamation point, Bash uses the value of the variable formed from the rest of PARAMETER as the name of the variable; this variable is then expanded and that value is used in the rest of the substitution, rather than the value of PARAMETER itself. This is known as indirect expansion.

The example given is:

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

I don't quite understand here:

the value of the variable formed from the rest of PARAMETER

As the PARAMETER is just !N*, then

the rest of PARAMETER

is just N*. How could this form a variable? Did Bash search all possible commands there?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

梅倚清风 2024-12-28 02:31:32

如果您阅读了 bash 手册页,它基本上证实了您的说法:

如果参数的第一个字符是感叹号(!),则引入一级变量间接寻址。 Bash 使用由参数的其余部分形成的变量的值作为变量的名称;然后扩展该变量,并在其余替换中使用该值,而不是参数本身的值。这称为间接扩展。

然而,从那里继续阅读:

例外情况是下面描述的 ${!prefix*}${!name[@]} 的扩展。

${!prefix*} 名称匹配前缀。扩展为名称以前缀开头的变量名称,并以 IFS 特殊变量的第一个字符分隔。

换句话说,您的特定示例 ${!N*} 是您引用的规则的例外。然而,它确实在预期的情况下发挥作用,例如:

$ export xyzzy=plugh ; export plugh=cave

$ echo ${xyzzy}  # normal, xyzzy to plugh
plugh

$ echo ${!xyzzy} # indirection, xyzzy to plugh to cave
cave

${!prefix*} 的示例:

$ xyzzy=plugh
$ xyzzz=flume

$ echo ${!xyzz*}
xyzzy xyzzz

If you read the bash man page, it basically confirms what you have stated:

If the first character of parameter is an exclamation point (!), a level of variable indirection is introduced. Bash uses the value of the variable formed from the rest of parameter as the name of the variable; this variable is then expanded and that value is used in the rest of the substitution, rather than the value of parameter itself. This is known as indirect expansion.

However, reading on from there:

The exceptions to this are the expansions of ${!prefix*} and ${!name[@]} described below.

${!prefix*} Names matching prefix. Expands to the names of variables whose names begin with prefix, separated by the first character of the IFS special variable.

In other words, your particular example ${!N*} is an exception to the rule you quoted. It does, however, work as advertised in the expected cases, such as:

$ export xyzzy=plugh ; export plugh=cave

$ echo ${xyzzy}  # normal, xyzzy to plugh
plugh

$ echo ${!xyzzy} # indirection, xyzzy to plugh to cave
cave

An example of ${!prefix*}:

$ xyzzy=plugh
$ xyzzz=flume

$ echo ${!xyzz*}
xyzzy xyzzz
叫思念不要吵 2024-12-28 02:31:32

当给定的“间接”以 * 结尾时,似乎存在异常,就像这里一样。在本例中,它给出以您指定的部分开头的所有变量名称(此处为N)。
Bash 可以做到这一点,因为它跟踪变量并知道哪些变量存在。

真正的间接是这样的:
假设我将一个变量 $VARIABLE 设置为 42,并将另一个变量 $NAME 设置为 VARIABLE${!NAME} 会给我 42。您可以使用一个变量的值来告诉您另一个变量的名称:

$ NAME="VARIABLE"
$ VARIABLE=42
$ echo ${!NAME}
42

There appears to be an exception when the given "indirection" ends in a *, as it does here. In this case, it gives all variable names that start with the part you specified (N here).
Bash can do that because it tracks variables and knows which ones exist.

True indirection is this:
Say I have a variable $VARIABLE set to 42, and I have another variable $NAME set to VARIABLE. ${!NAME} will give me 42. You use the value of one variable to tell you the name of another:

$ NAME="VARIABLE"
$ VARIABLE=42
$ echo ${!NAME}
42
最丧也最甜 2024-12-28 02:31:32

bash 间接 和/或 nameref

在这个问题上迟到了,因为没有其他答案讲述nameref...

使用 ${ !var} 间接语法:

~$ someVariable='Some content'
~$ var=someVariable

~$ echo $var
someVariable

~$ echo ${!var}
Some content

使用 namref (declare -n) 语法

通过使用 nameref 你不仅可以显示变量的内容,还可以填充变量并获取或设置属性。

~$ someVariable='Some content'
~$ declare -n var=someVariable
~$ echo $var
Some content
~$ echo ${var@A}
someVariable='Some content'

此语法对于函数很有用:(

function showVarDetail() {
    local -n var=$1
    printf 'Variable \47\44%s\47 is %d len, has [%s] flags and contain: %q\n' \
        "$1" "${#var}" "${var@a}" "$var"
}

注意:此函数只是一个示例。这不会正确扩展数组关联数组!)

然后

~$ someVar='Hello world!'
~$ showVarDetail someVar
Variable '$someVar' is 12 len, has [] flags and contain: Hello\ world\!

~$ declare -r PI=3.14159265358979323844
~$ showVarDetail PI
Variable '$PI' is 22 len, has [r] flags and contain: 3.14159265358979323844

~$ declare -ir answerOfUltimateQestionOfLiveUniverseAndEverything=42
~$ showVarDetail answerOfUltimateQestionOfLiveUniverseAndEverything
Variable '$answerOfUltimateQestionOfLiveUniverseAndEverything' is 2 len, has [ir
] flags and contain: 42

使用填充变量值>nameref

这可以有两种方式!

这是一个使用两个变量名作为参数运行的小示例函数。第一个变量应包含一个字符串,第二个变量将由第一个变量内容的第一个字符填充,然后第一个变量内容将移动 1 个字符:

shift1char <变量字符串源> <可变目标>
shift1char () { 
    local -n srcStr=$1 tgtVar=$2;
    tgtVar=${srcStr::1} srcStr=${srcStr:1}
}

然后

~$ someVar='Hello world!'

~$ shift1char someVar someChar

~$ showVarDetail someVar
Variable '$someVar' is 11 len, has [] flags and contain: ello\ world\!

~$ showVarDetail someChar
Variable '$someChar' is 1 len, has [] flags and contain: H

进行一些小的修改:

showVarDetail() { 
    local _nam
    for _nam in "$@"; do
        local -n _var=$_nam
        printf \
             'Variable \47\44%s\47 is %d len, has [%s] flags and contain: %q\n' \
             "${_nam}" "${#_var}" "${_var@a}" "$_var"
    done
}
move1char() {
    local -n srcStr=$1 tgtVar=$2
    [[ -z $srcStr ]] && return 1
    tgtVar+=${srcStr::1} srcStr=${srcStr:1}
}
someVar='Hello world!' target=''
while move1char someVar target;do
    showVarDetail someVar target
done

应该产生:

Variable '$someVar' is 11 len, has [] flags and contain: ello\ world\!
Variable '$target' is 1 len, has [] flags and contain: H
Variable '$someVar' is 10 len, has [] flags and contain: llo\ world\!
Variable '$target' is 2 len, has [] flags and contain: He
Variable '$someVar' is 9 len, has [] flags and contain: lo\ world\!
Variable '$target' is 3 len, has [] flags and contain: Hel
Variable '$someVar' is 8 len, has [] flags and contain: o\ world\!
Variable '$target' is 4 len, has [] flags and contain: Hell
Variable '$someVar' is 7 len, has [] flags and contain: \ world\!
Variable '$target' is 5 len, has [] flags and contain: Hello
Variable '$someVar' is 6 len, has [] flags and contain: world\!
Variable '$target' is 6 len, has [] flags and contain: Hello\ 
Variable '$someVar' is 5 len, has [] flags and contain: orld\!
Variable '$target' is 7 len, has [] flags and contain: Hello\ w
Variable '$someVar' is 4 len, has [] flags and contain: rld\!
Variable '$target' is 8 len, has [] flags and contain: Hello\ wo
Variable '$someVar' is 3 len, has [] flags and contain: ld\!
Variable '$target' is 9 len, has [] flags and contain: Hello\ wor
Variable '$someVar' is 2 len, has [] flags and contain: d\!
Variable '$target' is 10 len, has [] flags and contain: Hello\ worl
Variable '$someVar' is 1 len, has [] flags and contain: \!
Variable '$target' is 11 len, has [] flags and contain: Hello\ world
Variable '$someVar' is 0 len, has [] flags and contain: ''
Variable '$target' is 12 len, has [] flags and contain: Hello\ world\!

bash indirection and/or nameref

Comming late on this question, and because no other answer tell about nameref...

Using ${!var} indirection syntax:

~$ someVariable='Some content'
~$ var=someVariable

~$ echo $var
someVariable

~$ echo ${!var}
Some content

Using namref (declare -n) syntax

By using nameref you could not only show content of variable, but you can populate variable and get or set attributes.

~$ someVariable='Some content'
~$ declare -n var=someVariable
~$ echo $var
Some content
~$ echo ${var@A}
someVariable='Some content'

This syntax is usefull for functions:

function showVarDetail() {
    local -n var=$1
    printf 'Variable \47\44%s\47 is %d len, has [%s] flags and contain: %q\n' \
        "$1" "${#var}" "${var@a}" "$var"
}

(Nota: This function is only a sample. This won't expand correctly arrays and associative arrays!)

Then

~$ someVar='Hello world!'
~$ showVarDetail someVar
Variable '$someVar' is 12 len, has [] flags and contain: Hello\ world\!

~$ declare -r PI=3.14159265358979323844
~$ showVarDetail PI
Variable '$PI' is 22 len, has [r] flags and contain: 3.14159265358979323844

~$ declare -ir answerOfUltimateQestionOfLiveUniverseAndEverything=42
~$ showVarDetail answerOfUltimateQestionOfLiveUniverseAndEverything
Variable '$answerOfUltimateQestionOfLiveUniverseAndEverything' is 2 len, has [ir
] flags and contain: 42

Populating variable values using nameref

This could work in both ways!

Here is a little sample function to run with two variable names as arguments. First variable should contain a string and second variable will be populated by 1st character of 1st variable content, then 1st variable content will be shifted by 1 character:

shift1char <variable string source> <variable target>
shift1char () { 
    local -n srcStr=$1 tgtVar=$2;
    tgtVar=${srcStr::1} srcStr=${srcStr:1}
}

Then

~$ someVar='Hello world!'

~$ shift1char someVar someChar

~$ showVarDetail someVar
Variable '$someVar' is 11 len, has [] flags and contain: ello\ world\!

~$ showVarDetail someChar
Variable '$someChar' is 1 len, has [] flags and contain: H

With some little modifications:

showVarDetail() { 
    local _nam
    for _nam in "$@"; do
        local -n _var=$_nam
        printf \
             'Variable \47\44%s\47 is %d len, has [%s] flags and contain: %q\n' \
             "${_nam}" "${#_var}" "${_var@a}" "$_var"
    done
}
move1char() {
    local -n srcStr=$1 tgtVar=$2
    [[ -z $srcStr ]] && return 1
    tgtVar+=${srcStr::1} srcStr=${srcStr:1}
}
someVar='Hello world!' target=''
while move1char someVar target;do
    showVarDetail someVar target
done

should produce:

Variable '$someVar' is 11 len, has [] flags and contain: ello\ world\!
Variable '$target' is 1 len, has [] flags and contain: H
Variable '$someVar' is 10 len, has [] flags and contain: llo\ world\!
Variable '$target' is 2 len, has [] flags and contain: He
Variable '$someVar' is 9 len, has [] flags and contain: lo\ world\!
Variable '$target' is 3 len, has [] flags and contain: Hel
Variable '$someVar' is 8 len, has [] flags and contain: o\ world\!
Variable '$target' is 4 len, has [] flags and contain: Hell
Variable '$someVar' is 7 len, has [] flags and contain: \ world\!
Variable '$target' is 5 len, has [] flags and contain: Hello
Variable '$someVar' is 6 len, has [] flags and contain: world\!
Variable '$target' is 6 len, has [] flags and contain: Hello\ 
Variable '$someVar' is 5 len, has [] flags and contain: orld\!
Variable '$target' is 7 len, has [] flags and contain: Hello\ w
Variable '$someVar' is 4 len, has [] flags and contain: rld\!
Variable '$target' is 8 len, has [] flags and contain: Hello\ wo
Variable '$someVar' is 3 len, has [] flags and contain: ld\!
Variable '$target' is 9 len, has [] flags and contain: Hello\ wor
Variable '$someVar' is 2 len, has [] flags and contain: d\!
Variable '$target' is 10 len, has [] flags and contain: Hello\ worl
Variable '$someVar' is 1 len, has [] flags and contain: \!
Variable '$target' is 11 len, has [] flags and contain: Hello\ world
Variable '$someVar' is 0 len, has [] flags and contain: ''
Variable '$target' is 12 len, has [] flags and contain: Hello\ world\!
晒暮凉 2024-12-28 02:31:32

是的,它会搜索 ! 之后所有可能的变量扩展。如果您已完成:

echo ${!NP*}

您将仅获得 NPX_PLUGIN_PATH

考虑以下示例:

:~> export myVar="hi"
:~> echo ${!my*}
    myVar
:~> export ${!my*}="bye"
:~> echo $myVar
    bye

Yes, it searches for all possible expansions of variables after the !. If you had done:

echo ${!NP*}

you would get only NPX_PLUGIN_PATH.

Consider the following example:

:~> export myVar="hi"
:~> echo ${!my*}
    myVar
:~> export ${!my*}="bye"
:~> echo $myVar
    bye
却一份温柔 2024-12-28 02:31:32

您在间接处理中遇到了异常,如果最后一个字符是 *,则将返回具有之前给定前缀的所有变量。

You've hit an exception in indirection processing, where if the last character is *, all variables that have the prefix given before will be returned.

情魔剑神 2024-12-28 02:31:32

您可以参考这个 GNU bash 文档来获取权威信息

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

但基本上是间接的作为例外之一,不会对 ${!prefix*} 执行扩展,在您的示例中,N 是前缀。

该文档将解释 bash 中的间接扩展是什么

You can refer to this GNU doc for bash for authoritative information

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

But basically, indirect expansion is not performed on ${!prefix*} as one of the exceptions, in your example, N is the prefix.

The Document will explain what indirect expansion is in bash

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文