Bash 程序退出后执行 EXIT 陷阱时的函数作用域状态!= 0 (set -e)
Bash function scope status when EXIT trap is executed after a program exits with status != 0 (set -e)
在 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_two中被声明为局部且只读的,也可以从函数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"
func_one
}
func_two
输出为:
var from func_one
如果从 EXIT 陷阱调用 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 陷阱(如果命令以非零状态退出,则设置 -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
任何人都可以向我解释为什么会这样吗?提前谢谢你。
这是 Bash 中的错误。
当您最初将 func_one
安装为退出处理程序时,Bash 在脚本末尾调用它,在 func_two
已 returned 之后。一切顺利。
当您使用 set -e
的组合并从 func_one
调用 false
时,Bash 退出脚本并调用退出处理程序,在调用false
,换句话说,在 func_one
.
内
Bash 通过调用 longjmp
到 return 控制到顶级解析器,传递代码 ERREXIT
来实现 "exit on error"。在处理这种情况的代码中,有一条注释表明脚本应该忘记任何正在执行的函数,它通过将变量 variable_context
设置为 0
来实现。看起来 variable_context
是一堆命名范围的索引,将其设置回 0
将其指向顶级全局范围。
接下来,Bash 调用陷阱处理程序,后者调用 func_one
。现在 variable_context
是 1
,即它在 func_two
中的值相同。当脚本尝试设置 var
时,Bash 查看在此上下文中定义的名称并发现 var
已经存在,是 func_two
.[=47= 遗留下来的]
我在调试器中确认了这一点,并且还有一个解决方法:如果你添加一个中间函数调用,脚本就可以工作,因为现在在 func_one
中,variable_context
是 2
并且Bash 不再看到 func_two
剩下的 var
:
#!/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
return 的情况下工作并且能够在 Bash 调用 func_one
.
之前清理其变量的原因
更新:看起来 variable_context
是 而不是 堆栈的索引(它只是一个函数嵌套计数器),代码 mallocs new space 输入函数时的变量?所以不是 100% 确定这里到底发生了什么,但是 Bash 确实在 func_one
中找到了 var
的 func_two
版本,并且添加中间调用可以使问题消失,因此,由于 "exit on error" 设置并导致 func_one
继承其变量,Bash 在 func_two
之后未清理是某种问题。
在 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_two中被声明为局部且只读的,也可以从函数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"
func_one
}
func_two
输出为:
var from func_one
如果从 EXIT 陷阱调用 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 陷阱(如果命令以非零状态退出,则设置 -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
任何人都可以向我解释为什么会这样吗?提前谢谢你。
这是 Bash 中的错误。
当您最初将 func_one
安装为退出处理程序时,Bash 在脚本末尾调用它,在 func_two
已 returned 之后。一切顺利。
当您使用 set -e
的组合并从 func_one
调用 false
时,Bash 退出脚本并调用退出处理程序,在调用false
,换句话说,在 func_one
.
Bash 通过调用 longjmp
到 return 控制到顶级解析器,传递代码 ERREXIT
来实现 "exit on error"。在处理这种情况的代码中,有一条注释表明脚本应该忘记任何正在执行的函数,它通过将变量 variable_context
设置为 0
来实现。看起来 variable_context
是一堆命名范围的索引,将其设置回 0
将其指向顶级全局范围。
接下来,Bash 调用陷阱处理程序,后者调用 func_one
。现在 variable_context
是 1
,即它在 func_two
中的值相同。当脚本尝试设置 var
时,Bash 查看在此上下文中定义的名称并发现 var
已经存在,是 func_two
.[=47= 遗留下来的]
我在调试器中确认了这一点,并且还有一个解决方法:如果你添加一个中间函数调用,脚本就可以工作,因为现在在 func_one
中,variable_context
是 2
并且Bash 不再看到 func_two
剩下的 var
:
#!/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
return 的情况下工作并且能够在 Bash 调用 func_one
.
更新:看起来 variable_context
是 而不是 堆栈的索引(它只是一个函数嵌套计数器),代码 mallocs new space 输入函数时的变量?所以不是 100% 确定这里到底发生了什么,但是 Bash 确实在 func_one
中找到了 var
的 func_two
版本,并且添加中间调用可以使问题消失,因此,由于 "exit on error" 设置并导致 func_one
继承其变量,Bash 在 func_two
之后未清理是某种问题。