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

时间:2018-05-25 21:30:53

标签: linux bash

为了调试我的脚本,我想在每个输出的开头添加内部变量$ 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:我只是设置了一些东西" 我认为这样做是因为双引号只会在一次内部扩展变量。

在同一行中有两种语法清晰的方法来扩展变量吗?

3 个答案:

答案 0 :(得分:7)

处理运行时数据时,您无法安全两次评估扩展。

有办法进行重新评估,但它们需要信任您的数据 - 在NSA系统设计意义上的一句话:"可信组件是一个可以在系统失败时破坏您的系统的组件。

有关详细讨论,请参阅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)运行时,发布:

foo:7: I just set bar to baz

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

  

一个数组变量,其成员是源文件中的行号,其中调用了FUNCNAME的每个相应成员。

因此,FUNCNAME[0]trace,而FUNCNAME[1]foo;而BASH_LINENO[0]trace被调用的行 - 一行在函数foo内。

答案 1 :(得分:1)

虽然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任何来自外部来源的内容,因为你可以将一个命令注入到字符串中。

答案 2 :(得分:0)

以扩大两倍;但,它不会满足您的期望。

,bash提供了一种对变量进行“双重扩展”的方法,也就是一种首先解释变量,然后将其用作其他变量的名称的方法,其中另一个变量是实际要扩展的内容。这称为“间接”。通过“间接”,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'

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