在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,间接可以是一个强大的工具。
为了调试我的脚本,我想在每个输出的开头添加内部变量 $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,间接可以是一个强大的工具。