“陷阱……INT TERM EXIT”真的必要吗?


63

对于许多例子trap使用trap ... INT TERM EXIT的清理任务。但是是否真的有必要列出所有三个sigspec?

该手册说:

如果SIGNAL_SPEC为EXIT(0),则从外壳退出时将执行ARG。

我认为无论脚本正常完成还是由于收到SIGINT或脚本而结束,该脚本都适用SIGTERM。实验还证实了我的信念:

$ cat ./trap-exit
#!/bin/bash
trap 'echo TRAP' EXIT
sleep 3
$ ./trap-exit & sleep 1; kill -INT %1
[1] 759
TRAP
[1]+  Interrupt               ./trap-exit
$ ./trap-exit & sleep 1; kill -TERM %1
[1] 773
TRAP
[1]+  Terminated              ./trap-exit

那为什么这么多例子列出了全部INT TERM EXIT呢?还是我错过了某件事,是否有鞋底EXIT会错过的情况?


3
还请记住,INT TERM EXIT在收到SIGTERM或的条件下,清理代码会执行两次SIGINT
maxschlepzig

Answers:


19

POSIX规范并没有说太多关于导致执行EXIT陷阱,只有它的环境必须考虑什么是执行时它像的条件。

在Busybox的白蜡外壳中,由于SIGINT或SIGTERM,您的陷阱退出测试在退出之前不会回显“ TRAP”。我怀疑存在其他可能无法正常运行的外壳。

# /tmp/test.sh & sleep 1; kill -INT %1
# 
[1]+  Interrupt                  /tmp/test.sh
# 
# 
# /tmp/test.sh & sleep 1; kill -TERM %1
# 
[1]+  Terminated                 /tmp/test.sh
# 

3
dash也不会EXIT在收到消息时就陷入陷阱SIGINT/SIGTERM
maxschlepzig

4
zsh以及-因此,也许bash是唯一EXIT也可以匹配信号的外壳。
maxschlepzig

@maxschlepzig zsh不会在EXIT接收时陷入陷阱INT,但在接收时会陷入陷阱TERM。编辑:我只是注意到这有多大了……
JoL

27

是,有一点不同。

当您按下该脚本将退出Enter,或者发送SIGINTSIGTERM

trap '' EXIT
echo ' --- press ENTER to close --- '
read response

按下时,该脚本将退出Enter

trap '' EXIT INT TERM
echo ' --- press ENTER to close --- '
read response

*在shBashZsh中测试。(当您添加要运行的陷阱的命令时,在sh中不再起作用)


@Shawn也说了什么:AshDash不会使用捕获信号EXIT

因此,要稳健地处理信号,最好避免EXIT完全陷入陷阱,并使用如下所示的内容:

cleanup() {
    echo "Cleaning stuff up..."
    exit
}

trap cleanup INT TERM
echo ' --- press ENTER to close --- '
read var
cleanup

1
清理解决方案做对了-非常优雅!它已经成为我的bash脚本mktemp调用的惯用法。
Bjoern Dahlgren'1

2
这是exit必需的cleanup吗?
jarno

3
如果您的代码中有ShellScript错误导致它过早退出,则此方法将无效。
ijw

2
@ijw:在Bash和Ksh中,您可以捕获它ERR来处理它,但是它不是可移植的
Zaz

5
当另一个外壳调用它时,此解决方案并不可靠。它不处理合作出口的等待 ; 您将需要trap - INT TERM; kill -2 $$作为清理的最后一行,告诉父shell它过早退出。如果父外壳foobar.sh调用了脚本(foo.sh),然后又调用了bar.sh,那么如果将INT / TERM发送到foo.sh,则您不希望bar.sh执行。 trap cleanup EXIT将自动处理此传播,因此它是IMO中最强大的。这也意味着您不必cleanup在脚本末尾调用。
Nicholas Pipitone

12

完善最后一个答案,因为它存在以下问题:

# Our general exit handler
cleanup() {
    err=$?
    echo "Cleaning stuff up..."
    trap '' EXIT INT TERM
    exit $err 
}
sig_cleanup() {
    trap '' EXIT # some shells will call EXIT after the INT handler
    false # sets $?
    cleanup
}
trap cleanup EXIT
trap sig_cleanup INT QUIT TERM

以上几点:

在我测试时,INT和TERM处理程序不会为我退出-它们会处理错误,然后Shell返回到退出状态(这并不奇怪)。因此,我确保清除操作随后会退出,并且在信号的情况下始终使用错误代码(在正常退出的情况下,则保留错误代码)。

使用bash时,似乎在INT处理程序中退出还会调用EXIT处理程序,因此我解开了退出处理程序并自己调用它(无论行为如何,它都可以在任何shell中工作)。

我陷阱退出是因为shell脚本可以在到达底部之前退出-语法错误,set -e和非零返回,只需调用exit。您不能依靠shell脚本走到底。

如果您从未尝试过,SIGQUIT就是Ctrl- \。为您提供奖金的核心转储。因此,即使有点晦涩难懂,我也认为这值得一试。

过去的经验表明,如果您(像我一样)总是按几次Ctrl-C,您有时会在shell脚本的清理部分中途将其捕获,因此此方法有效,但并不总是如您所愿。


2
无论导致退出的信号是什么,调用者都将获得1作为退出代码,而trap调用者将获得SIGINT为130,SIGTERM为143等。因此,我将捕获并传递正确的退出代码为:sig_cleanup() { err=$?; trap '' EXIT; (exit $err); cleanup; }
musiphil

2
您能否阐明trap '' EXIT INT TERM清除功能的目的?这是否是为了防止用户意外中断您在上一段中提到的清理工作?是EXIT多余的吗?
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.