在 dash vs ksh 和 bash 中退出陷阱

PSk*_*cik 9 bash ksh dash trap

这是一个简单的脚本,它在当前目录中设置一个临时目录,并在退出时设置一个陷阱以将其删除。

#filename: script   
set -x  
trap 'rm -rf "$d"' exit
d=`TMPDIR=$PWD mktemp -d`
"$@"
Run Code Online (Sandbox Code Playgroud)

如果我执行ksh script sleep 100bash script sleep 100中断它C-C,则执行陷阱并删除目录。它不适用于dash. 为什么?这是错误还是预期行为?

Sté*_*las 19

zsh, pdksh(虽然不是mksh从它派生的最新版本),yashBourne shell 的行为类似于dash.

只有bashksh88ksh93mksh其他行为。

POSIX 规范并不清楚什么应该是正确的行为,但其中没有任何内容表明允许 shell 覆盖SIGINT(或其他)信号的默认处理程序。

它说EXIT应该在exit调用时评估陷阱动作,但是 AFAICT,它甚至没有说,例如,是否应该在 shell 退出时评估它,因为set -eset -u或错误条件(如语法错误或特殊内置失败)。

为了能够EXIT在接收到信号时运行陷阱,shell 需要在该信号上安装一个处理程序。

这就是ksh,mkshbashdo ,但是它们处理的信号列表在所有三个实现之间是不同的。所有 3 种之间唯一共有的信号似乎是INT, QUIT, TERM,ALRMHUP

如果您希望EXIT陷阱在某些信号上运行,可移植的方法是自己处理这些信号:

trap 'exit 1' INT HUP QUIT TERM ALRM USR1
trap 'cleanup' EXIT
Run Code Online (Sandbox Code Playgroud)

然而,这种方法不适用于zshEXIT如果exit从陷阱处理程序调用它不会运行陷阱。

它也没有向您的父母报告您的死亡信号。

因此,您可以这样做:

for sig in INT QUIT HUP TERM ALRM USR1; do
  trap "
    cleanup
    trap - $sig EXIT
    kill -s $sig "'"$$"' "$sig"
done
trap cleanup EXIT
Run Code Online (Sandbox Code Playgroud)

现在,请注意,如果在您执行时有更多信号到达cleanup,则cleanup可能会再次运行。cleanup如果多次调用和/或在执行期间忽略信号,您可能希望确保您的工作正常。