在bash函数中声明局部变量使该变量仅在函数本身及其子级内部可见,所以如果我运行:
#!/bin/bash
set -e
func_one() {
echo "${var}"
}
func_two() {
local -r var="var from func_two"
func_one
}
func_two
输出为:
var from func_two
即使将 var 变量声明为局部变量,并且可以从函数 func_one 中访问 func_two 中的只读变量。在后者中,可以声明一个具有本地和只读名称的变量:
#!/bin/bash
set -e
func_one() {
local -r var="var from func_one"
echo "${var}"
}
func_two() {
local -r var="var from func_two"
func_one
}
func_two
输出为:
var from func_one
如果从退出陷阱中调用 func_one ,也会发生同样的情况:
#!/bin/bash
set -e
func_one() {
local -r var="var from func_one"
echo "${var}"
}
func_two() {
local -r var="var from func_two"
trap 'func_one' EXIT
echo "${var}"
}
func_two
运行我收到的代码:
var from func_two
var from func_one
但是,如果在错误后执行EXIT陷阱(如果命令以非零状态退出,则set -e选项会使脚本立即退出)。似乎无法在 func_one 中重新分配var变量:
#!/bin/bash
set -e
func_one() {
local -r var="var from func_one"
echo "${var}"
}
func_two() {
local -r var="var from func_two"
trap 'func_one' EXIT
echo "${var}"
false
}
func_two
运行我收到的代码:
var from func_two
local: var: readonly variable
任何人都可以告诉我为什么会这样吗?预先谢谢你。
答案 0 :(得分:2)
这是Bash中的错误。
最初安装func_one
作为出口处理程序时,Bash在返回func_two
后在脚本末尾调用它。一切都很好。
当您使用set -e
的组合并从false
调用func_one
时,Bash会退出脚本,并在调用false
之后调用退出处理程序,换句话说,在func_one
之内。
Bash通过调用longjmp
并传递代码ERREXIT
来调用variable_context
,以将控制权返回给顶级解析器,从而实现“错误退出”。在处理这种情况的代码中,有一段注释表明脚本应该忘记正在执行的任何功能,这是通过将变量0
设置为variable_context
来实现的。似乎0
是指向命名作用域堆栈的索引,将其设置回func_one
会将其指向顶级全局作用域。
接下来,Bash调用陷阱处理程序,该陷阱处理程序调用{{1}}。现在variable_context
是1
,也就是说,它与func_two
中的值相同。当脚本尝试设置var
时,Bash查看在此上下文中定义的名称,并发现var
剩下的func_two
已经存在。
我已在调试器中确认了这一点,并提供了一种解决方法:如果您添加一个中间函数调用,该脚本将起作用,因为现在在func_one
中,variable_context
是2
,而Bash没有不再看到var
剩下的func_two
:
#!/bin/bash
set -e
func_one() {
local -r var="var from func_one"
echo "${var}"
}
func_intermediate() {
func_one
}
func_two() {
local -r var="var from func_two"
echo "${var}"
trap 'func_intermediate' EXIT
false
}
func_two
很明显,在Bash代码中,展开函数调用堆栈实际上涉及到删除变量(有一个名为kill_all_local_variables
的函数)。仅递减variable_context
(或将其设置为0
)是不够的。这就是为什么脚本在func_two
首先返回并能够在Bash调用func_one
之前清理其变量的情况下起作用的原因。
更新:看来variable_context
并不是堆栈中的索引(只是函数嵌套计数器),并且代码在输入函数时为变量分配了新空间?因此,不能100%确定这里的实际情况,但是Bash确实在func_two
内找到了var
的{{1}}版本,并且添加了中间调用使问题消失了,所以这有点Bash在func_one
之后由于“错误退出”设置而无法清除并导致func_two
继承其变量的问题而没有清理。