$ {!FOO}和zsh


13

${!FOO}在中执行双重替换bash,这意味着它接受FOO的(字符串)值并将其用作变量名。
zsh不支持此功能。

有没有一种方法,使这项工作同样在bashzsh

背景:

我有一个环境变量列表,例如

PATH MAIL EDITOR

并想先打印变量名,然后再打印它们的值。

这适用于bash但不适用zsh

for VAR in LIST
do
        echo $VAR
        echo ${!VAR}
done

应该可以通过某种方式实现“旧方法” eval,但是我无法使其正常工作:

for VAR in LIST
do
        echo $VAR
        echo `eval \$$VAR`
done

我永远也不会理解为什么我不能简单地进行像${${VAR}}或什至${${${VAR}}}需要这样的任意深度替换,因此对此做一个解释也很好。


1
哈!我认为zsh应该具有更多功能。
ЯрославРахматуллин

4
无需吹嘘bash,它确实具有相同的功能(还有更多功能),仅使用不同的模式,即${(p)FOO}
Profpatsch 2013年

2
@ЯрославРахматуллин,zsh的(e)参数扩展标志已添加到zsh-2.6-beta17(1996年5月)中,该(P)标志已添加到3.1.5-pws-7(1999年)中。bash的${!var}2.0版本(1996年12月)。
斯特凡Chazelas

Answers:


18

bash和zsh都有执行间接扩展的方法,但是它们使用不同的语法。

使用eval;执行间接扩展非常容易。这适用于所有POSIX和大多数Bourne外壳。如果值包含在外壳程序中具有特殊含义的字符,请务必正确引用。

eval "value=\"\${$VAR}\""
echo "$VAR"
echo "$value"

${${VAR}}不起作用,因为它不是任何Shell都实现的功能。大括号内的内容必须符合不包含的语法规则${VAR}。(在zsh中,这是受支持的语法,但做了一些不同的操作:嵌套替换对相同的值执行连续的转换;${${VAR}}这等效于$VAR因为它对值执行两次身份转换。)


19

Profpatsch在原始问题下的注释给出了${(p)FOO}输出间接变量引用内容的示例。该标志不正确或错字,它应该是大写字母P,而不是小写字母p。使用:${(P)FOO}

以下应该产生所需的输出:

#! /bin/zsh
LIST=(PATH MAIL EDITOR)
for VAR in ${LIST}
do
    print "${VAR}:  ${(P)VAR}"
done

zshexpn手册页中;部分- 参数扩展标志

P

  This forces the value of the parameter name to be interpreted as a further
  parameter name,  whose  value  will  be used where appropriate.  Note that
  flags set with one of the typeset family of commands (in particular case
  transformations) are not applied to the value of name used in this fashion.

  If used with a nested parameter or command substitution, the result of that
  will be taken as a parameter  name  in the  same  way.   For  example,  if
  you  have  `foo=bar'  and `bar=baz', the strings ${(P)foo}, ${(P)${foo}}, and
  ${(P)$(echo bar)} will be expanded to `baz'

一次我读到为什么${${${VAR}}}不产生您期望的输出,但是此时我找不到它。您可以执行以下操作:

first="second" ; second="third" ; third="fourth" ; fourth="fifth"
print ${(P)${(P)${(P)first}}}
fifth

3

您没有正确使用eval。在您的示例中,$ VAR值前面带有“ $”(即“ $ VALUE”)将作为命令执行。那不是你想要的。您要评估一个变量的扩展,该变量的名称来自另一个变量。

$ for i in `echo PATH MAIL EDITOR`; 
    do eval moo="\${$i}" 
    echo $moo 
done
/usr/local/sbin:/usr/local/bin:/usr/sbin:/u (...)
/var/mail/root
nano

0

{ba,z}sh

这是一个在{ba,z} sh中都可以使用的函数。我相信它也符合POSIX。

当给出警告:

  • 空输入
  • 一个以上的论点
  • 未设置的变量名

# 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 [ "$#" -ne 1 ] || [ -z "${1-}" ]; then
    printf 'var_expand: expected one non-empty argument\n' >&2;
    return 1;
  fi
  eval printf '%s' "\"\${$1?}\""
}

有没有function,也没有[[...]]在POSIX sh
斯特凡Chazelas

不带参数的检查应与[ "$#" -eq 0 ](或可能正[ "$#" -ne 1 ]因为您只期望一个参数)一起使用。
斯特凡Chazelas

@StéphaneChazelas谢谢,更新了。是否有符合POSIX的参考外壳?我认为shell-check可能也有帮助。
汤姆·黑尔

@StéphaneChazelas重新测试,我也想在给定单个null参数时失败。我添加了您的第二条建议。干杯!
汤姆·黑尔

我同意引用${1-}给定a='"" -o -n 1' [ -z $a ]== 0。但是会不会$#总是一个令牌?
汤姆·黑尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.