除了 EXIT,是否需要指定其他陷阱?

47

我看到很多 shell 脚本都有以下代码:

trap cmd 0 1 2 3 13 15 # EXIT HUP INT QUIT PIPE TERM

在我能够访问的每个 shell 中,除了 0 信号以外的所有陷阱都是多余的,如果简单指定了信号陷阱,cmd 将会在接收到信号时执行:

trap cmd 0

后面的规范是足够的吗?还是有些 shell 需要指定其他信号?

3个回答

35
我认为在所有情况下,trap 0 在脚本终止之前执行,因此非常适用于清理功能(例如删除临时文件等)。其他信号可以有专门的错误处理,但应终止脚本(也就是调用 exit)。
我相信你所描述的实际上会让 cmd 执行两次。一次是为了处理信号(例如 SIGTERM),另一次是在退出时(trap 0)。
我认为正确的做法是像以下方式一样(请参见 POSIX 规范 trap):
trap "rm tmpfile" 0
trap "exit 1" TERM HUP ... 

这将确保脚本完成后会删除临时文件,并允许您在信号上设置自定义退出状态。 注意:无论是否遇到信号,都会调用trap 0。
如果您不关心设置退出状态,则trap 0就足够了。

1
使用 0 作为信号似乎文档记录非常不充分。而且它似乎没有“捕获” EXIT 信号。例如,我的脚本中的 trap "...cleanup..." 0ERR 上进行了清理,但在 EXIT 上没有。 - Josh M.

26
为了确保 EXIT 信号处理程序不会被执行两次(这几乎总是不希望的),它应始终在 EXIT 信号处理程序自身的定义中设置为已忽略或重置。
对于在程序中为它们定义了多个信号处理程序的信号也是如此。
# reset
trap 'excode=$?; cmd; trap - EXIT; echo $excode' EXIT HUP INT QUIT PIPE TERM

# ignore
trap 'excode=$?; trap "" EXIT; cmd; echo $excode' EXIT HUP INT QUIT PIPE TERM

这是一个好主意。我不认为有必要担心定义了多个陷阱的信号,因为每个信号只能定义一个陷阱。(第二个陷阱会替换第一个陷阱,而不是添加到堆栈中。)一些shell允许定义多个陷阱吗? - William Pursell
3
我和@WilliamPursell一样困惑,直到我最终注意到“忽略或者重置”。 - Ryne Everett

10

Shell标准没有规定当接收到未被捕获的信号时是否执行对0的陷阱。 特别是,bash和dash的行为不同。 如果没有设置任何信号的陷阱,则给定trap cmd-list 0,bash将在接收到SIGTERM时执行cmd-list,但dash不会执行。 如果给定trap cmd-list 0 2,则bash在接收到SIGTERM时执行一次cmd-list,而dash执行两次cmd-list。


4
最好的答案是结合了这个答案和Brandon Horsley的答案:为了可移植性,不要假设 trap ... 0 将在收到 SIGTERM 时被执行,也不要假设它不会被执行。可以设置一个分开的 trap "exit 1" SIGTERM ... 来确保它会被执行。 - dubiousjim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接