bash 和 zsh 中的双重和三重替换

Pro*_*sch 31 bash zsh variable-substitution

跟进此问题中的背景部分。

bashI 可以${!FOO}用于双重替换,在zsh ${(P)FOO}. 在这两种情况下,老派(hack-y)都eval \$$FOO有效。

所以,对我来说最聪明和最合乎逻辑的事情是${${FOO}}, ${${${FOO}}}…双/三/n 替换。为什么这不能按预期工作?

二:什么是\在做eval陈述?我认为这是一种逃避,使事情eval \$$$FOO变得不可能。如何使用在每个 shell 中都有效的三重 / n 替换?

cho*_*oba 23

\必须用于防止的膨胀$$(当前进程id)。对于三重替换,您需要双重 eval,因此也需要更多的转义以避免每个 eval 中不需要的扩展:

#! /bin/bash
l0=value
l1=l0
l2=l1
l3=l2
l4=l3

echo $l0
eval echo \$$l1
eval eval echo \\$\$$l2
eval eval eval echo \\\\$\\$\$$l3
eval eval eval eval echo \\\\\\\\$\\\\$\\$\$$l4
Run Code Online (Sandbox Code Playgroud)

  • 为什么我不能做类似 `eval $(eval echo \$$l2)` 的事情?我讨厌 bash,这完全没有意义。 (2认同)
  • @Profpatsch:简单。在每一步中,反斜杠的数量减半(所以实际上是 2ⁿ)。 (2认同)

小智 14

#!/bin/bash

hello=world
echo=hello

echo $echo ${!echo}
Run Code Online (Sandbox Code Playgroud)


Gil*_*il' 8

假设 的值FOO是一个有效的变量名(比如BAR),eval \$$FOO将 的值拆分BAR为单独的单词,将每个单词视为通配符模式,并将结果的第一个单词作为命令执行,将其他单词作为参数传递。美元前面的反斜杠使它按字面意思处理,因此传递给eval内置函数的参数是四字符 string $BAR

${${FOO}}是语法错误。它不会进行“双重替换”,因为在任何常见的 shell 中都没有这样的功能(无论如何都没有这种语法)。在 zsh 中,${${FOO}} 有效的并且是双重替换,但它的行为与您想要的不同:它对 的值执行两次连续转换FOO,这两个转换都是恒等转换,因此它只是一种奇特的写作方式${FOO}

如果要将变量的值视为变量,请注意正确引用事物。如果将结果设置为变量会容易得多:

eval "value=\${$FOO}"
Run Code Online (Sandbox Code Playgroud)

  • 将其设置为变量,以便第一个单词不用作命令?哎呀,这种逻辑很难理解。这是我在 bash 中遇到的普遍问题。 (2认同)

Tom*_*ale 6

{ba,z}sh 解决方案

这是一个在两者中都有效的函数{ba,z}sh。我相信它也符合 POSIX 标准。

不用为引用发疯,您可以将它用于多个间接级别,如下所示:

$ a=b
$ b=c
$ c=d
$ echo $(var_expand $(var_expand $a)
d
Run Code Online (Sandbox Code Playgroud)

或者,如果您有更多(!?!)间接级别,您可以使用循环。

它在给出时发出警告:

  • 空输入
  • 未设置的变量名
# Expand the variable named by $1 into its value. Works in both {ba,z}sh
# eg: a=HOME $(var_expand $a) == /home/me
var_expand() {
  if [ -z "${1-}" ] || [ $# -ne 1 ]; then
    printf 'var_expand: expected one argument\n' >&2;
    return 1;
  fi
  eval printf '%s' "\"\${$1?}\""
}
Run Code Online (Sandbox Code Playgroud)