为什么bash子shell中的退出陷阱不总是被调用?
我在bash中看到一些奇怪的行为,并在子shell中捕获为什么bash子shell中的退出陷阱不总是被调用?,bash,shell,rvm,Bash,Shell,Rvm,我在bash中看到一些奇怪的行为,并在子shell中捕获EXIT。我希望以下四行都输出相同的内容(“hi-trapped”): 前三个打印“hi-trapped”,但不是最后一个。它只输出“嗨”。陷阱没有被调用。您可以使用set-x验证这一点: set -x; a=$(trap 'echo trapped' EXIT ; echo hi); set +x; echo $a set -x; a=$(trap 'echo trapped' EXIT && echo hi); set
EXIT
。我希望以下四行都输出相同的内容(“hi-trapped”):
前三个打印“hi-trapped”,但不是最后一个。它只输出“嗨”。陷阱没有被调用。您可以使用set-x
验证这一点:
set -x; a=$(trap 'echo trapped' EXIT ; echo hi); set +x; echo $a
set -x; a=$(trap 'echo trapped' EXIT && echo hi); set +x; echo $a
set -x; a=$(trap 'echo trapped' EXIT ; /bin/echo hi); set +x; echo $a
set -x; a=$(trap 'echo trapped' EXIT && /bin/echo hi); set +x; echo $a
通过一些尝试和错误,我发现在以下情况下不会调用EXIT
陷阱:
&&
链接在一起。
- 如果您使用
,甚至在任何时候,陷阱都会执行
- 如果任何一个命令(最后一个命令除外)以非零退出状态退出,使得最后一个命令永远不会执行,则陷阱将执行
- 非最终命令可以是内置命令或函数,只要最终命令是程序,陷阱就不会运行
cd
,最后在EXIT
上添加了一个陷阱,它(除其他外)echo-n“保存会话…”
。我运行的是一个shell脚本:
因此,
some_dir
得到了附加的“Saving session…”。调试很困难,因为子shell并不总是运行rvm添加的EXIT
陷阱。我使用strace-e clone,execve-f-p$$&
来查看当前shell在运行echo版本和/bin/echo版本时正在做什么。我放置了一个&
,以便它继续读取命令
在/bin/echo版本中,我相信bash做了一个快捷方式,并为/bin/echo执行了()子shell,因此陷阱不再存在了(我想陷阱在execve中不会存在)
在bare echo版本中,它是一个内置的shell,因此不需要execve,因此当前()子shell作为shell退出,并调用trap
现在,另一件奇怪的事情是,如果我这样做:bash-c'a=$(trap“echo-trapped”EXIT&&/bin/echo-hi);echo$a'
,您将看到它被捕获了
我猜这是因为bash只在交互模式下进行快捷操作。批处理模式和交互模式之间的另一个示例差异是x的
(以美元计)(序号130);睡眠1;完成
。如果您在终端中输入它,并立即按C-z,然后使用fg
将其带回来,您将看到它将立即退出--其余的睡眠将被跳过。如果你把它放在一个脚本中,C-z,fg,它将继续休眠剩下的循环。你的观察是正确的,请参阅可能会尝试将stderr也添加到null,/dev/null 2>&1
@Inian,这显然是相关的,但我的场景并不取决于命令的错误状态(或者它们是否符合简单命令的条件),但取决于列表中的最后一个命令是内置/函数还是系统命令。关于重定向stderr。那没用。将cd
重定向到/dev/null
实际上是不必要的,而不是问题的一部分。在所有命令(在cd
之后和pwd
之后)之后运行EXIT
陷阱。这是写给史都的信。所以没有办法避免它。除了关闭stdout,或者在子shell中的最后一个命令之后将stdout重定向到/dev/null
。然而,这仍然不能解释为什么陷阱有时被执行,有时不被执行。
set -x; a=$(trap 'echo trapped' EXIT ; echo hi); set +x; echo $a
set -x; a=$(trap 'echo trapped' EXIT && echo hi); set +x; echo $a
set -x; a=$(trap 'echo trapped' EXIT ; /bin/echo hi); set +x; echo $a
set -x; a=$(trap 'echo trapped' EXIT && /bin/echo hi); set +x; echo $a
some_dir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" > /dev/null && pwd )