我有一个函数(=被调用者),它应该在其调用者中有效地声明和分配几个变量。它还应该能够知道呼叫者的名字是什么。
eval
现在,我通过向传递的变量返回一个由调用者编辑的字符串来实现前者。
write_local_var_assignments variable_name; eval "$variable_name"
"$FUNCNAME"
我想我可以通过让调用者传递或让被调用者调用caller
内置函数并解析其输出来实现后者。
所有这些解决方案看起来都很笨拙,所以我有两个问题:
- 被调用者是否可以在没有调用者合作的情况下将局部变量分配到调用者的上下文中?
即,我可以压缩:
write_local_var_assignments variable_name; eval "$variable_name"
进入刚刚
run_local_var_assignments
?
- 有没有更好的方法来获取函数调用者的名称?无需解析或命令替换即可直接获取结果会很好。
答案1
在bash
(and ksh88
, mksh
, yash
, dash
, zsh
) 中,局部变量作用域是动态的。
这段代码:
f() { a=2; echo "f: $a"; }
g() { local a=1; f; echo "g: $a"; }
a=0
g
echo "global: $a"
产生这个输出:
f: 2
g: 2
global: 0
这是f
updateg
的$a
变量,因为它是从 调用的g
。
这与使用 in 声明的变量形成对比,使用语法 ( ) intypeset
声明的函数或使用in声明的变量,您将得到:ksh
function f { ...; }
ksh93
private
zsh
f: 2
g: 1
global: 2
所以在这种情况下,您不需要执行任何操作。
要知道调用你的函数的名称,在 中bash
,你可以使用${FUNCNAME[1]}
。
等价zsh
的是$funcstack[2]
:
$ zsh -c 'f() { echo $funcstack[2]; }; g() { f; }; g'
g
$ bash -c 'f() { echo "${FUNCNAME[1]}"; }; g() { f; }; g'
g
答案2
如果您不介意使用命令替换,有一个函数可以让您将自我评估的字符串返回给调用者,从而允许您run_local_var_assignments
这样调用您创建的函数:
$(run_local_var_assignments)
该函数如下所示:
emit () {
local IFS=$'\n'
printf 'eval eval %q' "$*"
}
它发出一个 eval 语句,就像eval "$variable_name"
上面的一样。由于它是结果行中的第一件事,因此 bash 将发出的 eval 作为命令运行。
该函数完成的双重 eval 和 %q 转义只是在那里,因为它们是正确将换行符和其他特殊字符传递给调用者所必需的。
然后你的函数可以写成:
run_local_var_assignments () {
local assignments=()
assignments+=( 'local myvar1="my value1"' )
assignments+=( 'local myvar2="my value2"' )
emit "${assignments[@]}"
}
赋值的编写方式就好像它们是源代码一样,因此值中的空格之类的内容需要如上所述的引号。
在命令替换中调用此函数(如本答案的顶部)将在调用者的作用域中创建这些局部变量以及这些值。
您还可以使用常规字符串或定界符来代替语句数组,但我经常发现数组方法对于动态构建语句块很有用,所以我想我会展示这一点,尽管它稍微复杂一些。