在Bash中,有没有办法用双引号将变量展开两次?

In Bash, is there a way to expand variables twice in double quotes?

为了调试我的脚本,我想在每个输出的开头添加内部变量 $FUNCNAME 和 $LINENO,这样我就知道输出发生在哪个函数和行号上。

foo(){
    local bar="something"
    echo "$FUNCNAME $LINENO: I just set bar to $bar"
}

但是由于会有很多调试输出,如果我能做类似下面的事情会更干净:

foo(){
    local trace='$FUNCNAME $LINENO'
    local bar="something"
    echo "$trace: I just set bar to $bar"
}

但是上面的字面输出: “$FUNCNAME $LINENO:我只是设置了一些东西” 我认为这样做是因为双引号只在内部扩展一次变量。

是否有一种语法简洁的方法可以在同一行中将变量展开两次?

虽然 eval 有其危险,但它的作用是进行第二次扩展:

foo(){
    local trace='$FUNCNAME $LINENO'
    local bar="something"
    eval echo "$trace: I just set bar to $bar"
}

foo

给出:

foo 6: I just set bar to something

请注意不要 eval 任何来自外部来源的内容,因为您可能会将命令注入到字符串中。

处理运行时间数据时,您无法安全地计算两次扩展。

有重新评估的方法,但它们需要信任您的数据——在 NSA 系统设计意义上:"A trusted component is one that can break your system when it fails"。

有关详细讨论,请参阅 BashFAQ #48。请记住,如果您可以记录文件名,那么 除 NUL 之外的任何字符都可以出现在 UNIX 文件名中。 $(rm -rf ~)'$(rm -rf ~)'.txt 是合法名称。 * 是合法名称。

考虑一种不同的方法:

#!/usr/bin/env bash

trace() { echo "${FUNCNAME[1]}:${BASH_LINENO[0]}: $*" >&2; }

foo() {
        bar=baz
        trace "I just set bar to $bar"
}

foo

...当 运行 与 bash 4.4.19(1)-release 时,发出:

foo:7: I just set bar to baz

注意${BASH_LINENO[0]}${FUNCNAME[1]}的使用;这是因为 BASH_LINENO 定义如下:

An array variable whose members are the line numbers in source files where each corresponding member of FUNCNAME was invoked.

因此,FUNCNAME[0]trace,而FUNCNAME[1]foo;而 BASH_LINENO[0] 是调用 trace 的那一行——函数 foo.

内部的那一行

要双倍展开;但是,它不会如你所愿。

是的,bash 提供了一种对变量进行 "double expansion" 的方法,也就是一种首先解释变量,然后将其作为变量的方法其他变量的名称,其中其他变量是实际要扩展的变量。这叫做"indirection"。对于 "indirection",bash 允许 shell 变量引用另一个 shell 变量,最终值来自引用的变量。因此,bash 变量可以通过引用传递。

语法只是普通的大括号样式扩展,但在名称前加上感叹号。

${!VARNAME}

它是这样使用的:

BAR="my final value";
FOO=BAR
echo ${!FOO};

...产生此输出...

my final value

,您不能使用此机制来执行与 $( eval "echo $VAR1 $VAR2" 相同的操作。第一次解释的结果必须恰好是一个 shell 变量的名称。它不接受字符串,也不理解美元符号。所以这行不通:

BAR="my final value";
FOO='$BAR'; # The dollar sign confuses things
echo ${!FOO}; # Fails because there is no variable named '$BAR'

因此,它不会解决您的最终任务。 None-the-less,间接可以是一个强大的工具。