Bash 中复合变量的延迟扩展
Delayed expansion of composite variable in Bash
我将一个变量定义为其他变量和一些文本的组合,并且我试图让这个变量在赋值时不扩展它包含的变量。但我希望它在以后调用时扩展。这样我就可以重复使用相同的模板来打印不同的结果,因为内部变量不断变化。我正在努力避免 eval
尽可能多,因为我将从第三方收到一些内部变量,我不知道会发生什么。
我的用例,如下所示,是有一些“调用堆栈”,这样我就可以用相同的格式记录所有消息,并以某种格式记录脚本、函数和记录消息的行,比如这个:script.sh:this_function:42
.
我尝试的解决方案
called.sh:
#!/bin/bash
SCRIPT_NAME="`basename "${BASH_SOURCE[0]}"`"
CURR_STACK="${SCRIPT_NAME}:${FUNCNAME[0]}:${LINENO[0]}"
echo "${SCRIPT_NAME}:${FUNCNAME[0]}:${LINENO[0]}"
echo "${CURR_STACK}"
echo
function _func_1 {
echo "${SCRIPT_NAME}:${FUNCNAME[0]}:${LINENO[0]}"
echo "${CURR_STACK}"
}
_func_1
因此,我打算在打印 "${CURR_STACK}"
时获得与打印上一行时相同的结果。
如果有一些内置的或其他聪明的方法来记录这个 'call stack',一定要告诉我!我很乐意挥手告别我的代码,但我仍然想知道如何防止变量在分配 CURR_STACK
时立即扩展,但仍能使它们能够进一步扩展。
我是不是漏掉了一些 shopt
?
我尝试过的:
案例1(在第4行展开):
CURR_STACK="${SCRIPT_NAME}:${FUNNAME[0]}:${LINENO[0]}"
CURR_STACK="`echo "${SCRIPT_NAME}:${FUNCNAME[0]}:${LINENO[0]}"`"
CURR_STACK="`echo "${SCRIPT_NAME}:${FUNCNAME[0]}:${LINENO[0]}"`"
called.sh::7 <------------------| These are control lines
called.sh::4 <---------------. .------------| With the results I expect to get.
X
called.sh:_func_1:12 <---´ `-------| Both indicate that the values expanded
called.sh::4 <-------------------------| on line 4 - when CURR_STACK was set.
情况2(完全不展开):
CURR_STACK="${SCRIPT_NAME}:${FUNNAME[0]}:${LINENO[0]}"
CURR_STACK=${SCRIPT_NAME}:${FUNCNAME[0]}:${LINENO[0]}
CURR_STACK="`echo '${SCRIPT_NAME}:${FUNCNAME[0]}:${LINENO[0]}'`"
called.sh::7
${SCRIPT_NAME}:${FUNNAME[0]}:${LINENO[0]} <-------.----| No expansion at all!...
/
called.sh::12 /
${SCRIPT_NAME}:${FUNNAME[0]}:${LINENO[0]} <----´
你熟悉eval
吗?
$ a=this; b=is; c=a; d=test;
$ e='echo "$a $b $c $d"';
$ eval $e;
this is a test
$ b='is NOT'; # modify one of the variables
$ eval $e;
this is NOT a test
$ f=$(eval $e); # capture the value of the "eval" statement
$ echo $f;
this is NOT a test
Shell 变量存储的是纯惰性文本(*),不是可执行代码;这里实际上没有任何延迟评估的概念。要在使用时做一些事情,创建一个函数而不是变量:
print_curr_stack() {
echo "$(basename "${BASH_SOURCE[1]}"):${FUNCNAME[1]}:${BASH_LINENO[0]}"
}
# ...
echo "We are now at $(print_curr_stack)"
# Or just run it directly:
print_curr_stack
注意:使用 BASH_SOURCE[1]
和 FUNCNAME[1]
获取有关函数 运行 来自的上下文的信息,而不是它在函数本身中的位置。但出于某种原因我不清楚,BASH_LINENO[1]
得到了错误的信息,BASH_LINENO[0]
是你想要的。
您也可以编写它以允许调用者指定要打印的其他文本:
print_curr_stack() {
echo "$@" "$(basename "${BASH_SOURCE[1]}"):${FUNCNAME[1]}:${BASH_LINENO[0]}"
}
# ...
print_curr_stack "We are now at"
(* 我所说的变量只包含惰性文本有一个例外:一些变量——如 $LINENO
、$RANDOM
等——由 shell 本身。但是你不能像这样创建新的,除非修改 shell 本身。)
我将一个变量定义为其他变量和一些文本的组合,并且我试图让这个变量在赋值时不扩展它包含的变量。但我希望它在以后调用时扩展。这样我就可以重复使用相同的模板来打印不同的结果,因为内部变量不断变化。我正在努力避免 eval
尽可能多,因为我将从第三方收到一些内部变量,我不知道会发生什么。
我的用例,如下所示,是有一些“调用堆栈”,这样我就可以用相同的格式记录所有消息,并以某种格式记录脚本、函数和记录消息的行,比如这个:script.sh:this_function:42
.
我尝试的解决方案
called.sh:
#!/bin/bash
SCRIPT_NAME="`basename "${BASH_SOURCE[0]}"`"
CURR_STACK="${SCRIPT_NAME}:${FUNCNAME[0]}:${LINENO[0]}"
echo "${SCRIPT_NAME}:${FUNCNAME[0]}:${LINENO[0]}"
echo "${CURR_STACK}"
echo
function _func_1 {
echo "${SCRIPT_NAME}:${FUNCNAME[0]}:${LINENO[0]}"
echo "${CURR_STACK}"
}
_func_1
因此,我打算在打印 "${CURR_STACK}"
时获得与打印上一行时相同的结果。
如果有一些内置的或其他聪明的方法来记录这个 'call stack',一定要告诉我!我很乐意挥手告别我的代码,但我仍然想知道如何防止变量在分配 CURR_STACK
时立即扩展,但仍能使它们能够进一步扩展。
我是不是漏掉了一些 shopt
?
我尝试过的:
案例1(在第4行展开):
CURR_STACK="${SCRIPT_NAME}:${FUNNAME[0]}:${LINENO[0]}"
CURR_STACK="`echo "${SCRIPT_NAME}:${FUNCNAME[0]}:${LINENO[0]}"`"
CURR_STACK="`echo "${SCRIPT_NAME}:${FUNCNAME[0]}:${LINENO[0]}"`"
called.sh::7 <------------------| These are control lines
called.sh::4 <---------------. .------------| With the results I expect to get.
X
called.sh:_func_1:12 <---´ `-------| Both indicate that the values expanded
called.sh::4 <-------------------------| on line 4 - when CURR_STACK was set.
情况2(完全不展开):
CURR_STACK="${SCRIPT_NAME}:${FUNNAME[0]}:${LINENO[0]}"
CURR_STACK=${SCRIPT_NAME}:${FUNCNAME[0]}:${LINENO[0]}
CURR_STACK="`echo '${SCRIPT_NAME}:${FUNCNAME[0]}:${LINENO[0]}'`"
called.sh::7
${SCRIPT_NAME}:${FUNNAME[0]}:${LINENO[0]} <-------.----| No expansion at all!...
/
called.sh::12 /
${SCRIPT_NAME}:${FUNNAME[0]}:${LINENO[0]} <----´
你熟悉eval
吗?
$ a=this; b=is; c=a; d=test;
$ e='echo "$a $b $c $d"';
$ eval $e;
this is a test
$ b='is NOT'; # modify one of the variables
$ eval $e;
this is NOT a test
$ f=$(eval $e); # capture the value of the "eval" statement
$ echo $f;
this is NOT a test
Shell 变量存储的是纯惰性文本(*),不是可执行代码;这里实际上没有任何延迟评估的概念。要在使用时做一些事情,创建一个函数而不是变量:
print_curr_stack() {
echo "$(basename "${BASH_SOURCE[1]}"):${FUNCNAME[1]}:${BASH_LINENO[0]}"
}
# ...
echo "We are now at $(print_curr_stack)"
# Or just run it directly:
print_curr_stack
注意:使用 BASH_SOURCE[1]
和 FUNCNAME[1]
获取有关函数 运行 来自的上下文的信息,而不是它在函数本身中的位置。但出于某种原因我不清楚,BASH_LINENO[1]
得到了错误的信息,BASH_LINENO[0]
是你想要的。
您也可以编写它以允许调用者指定要打印的其他文本:
print_curr_stack() {
echo "$@" "$(basename "${BASH_SOURCE[1]}"):${FUNCNAME[1]}:${BASH_LINENO[0]}"
}
# ...
print_curr_stack "We are now at"
(* 我所说的变量只包含惰性文本有一个例外:一些变量——如 $LINENO
、$RANDOM
等——由 shell 本身。但是你不能像这样创建新的,除非修改 shell 本身。)