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

Linux 在Bash中,有没有办法用双引号将变量展开两次?,linux,bash,parameter-passing,variable-expansion,parameter-expansion,Linux,Bash,Parameter Passing,Variable Expansion,Parameter Expansion,为了调试脚本,我想在每个输出的开头添加内部变量$FUNCNAME和$LINENO,这样我就知道输出发生在哪个函数和行号上 foo(){ local bar="something" echo "$FUNCNAME $LINENO: I just set bar to $bar" } 但是,由于将有许多调试输出,如果我可以执行以下操作,将会更加干净: foo(){ local trace='$FUNCNAME $LINENO' local bar="somethin

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


是否有一种语法上干净的方法可以在同一行中两次扩展变量?

尽管
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系统设计的意义上,“受信任的组件是在系统出现故障时能够破坏系统的组件”

有关详细讨论,请参阅。请记住,如果可以记录文件名,则UNIX文件名中可以存在NUL以外的任何字符<代码>$(rm-rf~)'$(rm-rf~)'.txt是一个法定名称<代码>*是一个法定名称

考虑一种不同的方法: …当与bash 4.4.19(1)版本一起运行时,会发出:

foo:7: I just set bar to baz

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

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


因此,
FUNCNAME[0]
trace
,而
FUNCNAME[1]
foo
;而
BASH\u LINENO[0]
是调用
trace
的那一行——这一行位于函数
foo

Yes的内部,用于双重展开;但是,,它不会满足你的期望

,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'

因此,它不能解决你的终极任务。尽管如此,间接寻址还是一个强大的工具。

Oof。如果您的变量中只有一个变量名,那么就很容易了——在这种情况下,它只是标准的间接扩展。相比之下,如果您想用任意数量的变量扩展模板,那么您将进入
eval
空间,这显然是不安全的。几乎可以工作-如果
local trace=FUNCNAME
那么您可以编写
echo${!trace}”:我刚刚将bar设置为$bar“
@codefrester…这是一个相当繁重的标题编辑。完全从我(原始)答案f/e顶部的一行摘要中删除上下文,并将其用于根本不解决OP关于扩展的直接/直接问题的答案。我也这么认为。。。现在您已经告诉我了,我只是把它回滚了。不仅仅是命令注入风险——您也不能相信日志是准确的(因此,对于调试是有用的)。如果您设置
bar='*'
而不是
bar=something
,则引用将无法保存到
echo
,因此您将在日志中获得一个文件名列表。
eval'echo''$trace'':我只是将bar设置为$bar'
会更安全(仅扩展
$trace
,而不是字符串的其余部分,两次),虽然显然有一个易于使用的打击。@CharlesDuffy:接受。这个用例似乎是
eval
最初设计的目的,我觉得应该有人把它作为解决方案来展示。比
“*”
更糟糕的是
“rm-irf/”
我建议,如果有人能够破解东西来注入该命令,那么你就会遇到更大的问题。我们有很多关于如何使用间接寻址的问题,这可能是最典型的问题。是另一个伟大的资源。尽管如此,如果我相信这里的OP是在问一个关于这个主题的问题,我会把这个问题作为重复而不是回答。
BAR="my final value";
FOO='$BAR'; # The dollar sign confuses things
echo ${!FOO}; # Fails because there is no variable named '$BAR'