了解
如果 Bash 正在等待命令完成并接收到设置了陷阱的信号,则在命令完成之前不会执行陷阱。
当 Bash 通过 wait 内置函数等待异步命令时,接收到设置了陷阱的信号将导致 wait 内置函数立即返回,退出状态大于 128,在此之后陷阱将立即执行。
从 Bash 手册中,我运行以下命令:
在我的两个示例中,SIGINT(使用 Ctrl-C 发送)立即终止前台作业(引用中的第一种情况)和后台作业(引用中的第二种情况),无需等待它们完成。
引用中的第一句话是否意味着如果 Bash 正在运行前台作业并接收到信号SIGINT
,则信号的陷阱
SIGINT
在设置后将一直执行直到命令完成?如果是,为什么在我的第一个示例中,ctrl-C
使前台作业在完成之前立即存在?
$ sleep 10000 # a foreground job
^C
$ sleep 10000 & # a background job
[1] 21219
$ wait 21219
^C
$ echo $?
130
Run Code Online (Sandbox Code Playgroud)“已设置陷阱的信号”是什么 意思,
arg
已通过 指定陷阱的信号trap arg sigspec
,或
一个不被忽略的信号,或
一个信号的陷阱不是默认的?
在我的第 1 部分的示例中,我没有为 SIGINT 设置陷阱,因此信号有其默认处理程序(它会跳出任何执行循环)。是否认为具有默认处理程序的 信号已设置陷阱?
我为 设置了一个陷阱SIGINT
,但ctrl-C
会在完成之前退出以下命令。那么它是否与我引述中的第一句话相反?
$ trap "echo You hit control-C!" INT
$ bash -c 'sleep 10; echo "$?"'
^C
$
Run Code Online (Sandbox Code Playgroud)
在为 设置陷阱之前SIGINT
, ctrl-C
也会在完成之前使相同的命令退出。那么它是否与我引述中的第一句话相反?
$ bash -c 'sleep 10; echo "$?"'
^C
Run Code Online (Sandbox Code Playgroud)你能举一些例子来解释引文中的两句话是什么意思吗?
谢谢。
“已设置陷阱的信号”是什么意思?
这是一个已为其定义处理程序的信号(使用trap 'handling code' SIG
),其中处理代码不为空,因为这会导致信号被忽略。
因此,具有默认处置的信号不是已为其设置陷阱的信号。您帖子中的一些引用也适用于具有默认处置的信号,尽管显然不是关于运行 trap的部分,因为尚未为它们定义陷阱。
手册讨论信号传递到 shell,而不是你从那个 shell 运行的命令。
如果 Bash 正在等待命令完成并接收到设置了陷阱的信号,则在命令完成之前不会执行陷阱。
(1)
为什么在我的第一个示例中,ctrl-C 会在前台作业完成之前立即退出
如果您sleep 10
在交互式 shell 的提示下运行,shell 将把该作业放在前台(通过ioctl()
tty 设备上的一个告诉终端线路规则哪个进程组是前台),所以只会sleep
得到一个 SIGINT^C
并且交互式外壳不会,因此测试该行为没有用。
父 shell,因为它是交互式的,所以不会收到 SIGINT,因为它的进程不在前台进程组中。
每个命令都可以随意处理信号。sleep
不会对 SIGINT 做任何特别的事情,因此除非在启动时忽略 SIGINT,否则将获得默认处置(终止)。
(2)如果您sleep 10
在非交互式 shell 中运行,
bash -c 'sleep 10; echo "$?"'
Run Code Online (Sandbox Code Playgroud)
当您按下 Ctrl-C 时,非交互式bash
shell 和sleep
SIGINT 都会收到。
如果立即bash
退出,sleep
如果它碰巧忽略或处理 SIGINT 信号,它可能会使命令在后台无人看管。所以与其,
bash
像大多数其他 shell 一样,在等待命令时阻止接收信号(至少是某些信号)。在上面的那个例子中,sleep
将在 SIGINT 上消亡,所以bash
不必等待很长时间来处理它自己的 SIGINT(这里死了,因为我没有trap
在 SIGINT 上添加一个)。
(3) 当您在运行非交互式 shell 时按 Ctrl+C:
bash -c 'sh -c "trap \"\" INT; sleep 3"; echo "$?"'
Run Code Online (Sandbox Code Playgroud)
(trap
在 SIGINT上没有)bash
不会被 SIGINT 杀死。bash
,就像其他一些 shell 一样特别对待 SIGINT 和 SIGQUIT。它们实现了https://www.cons.org/cracauer/sigint.html 中描述的等待和合作退出行为(并且已知会引起一些烦恼,例如调用 SIGINT 处理命令的脚本不能被中断)^C
(4)要正确测试,你应该运行一个非交互式bash
的是具有SIGINT设下的陷阱,并调用一个不马上在SIGINT死命令,如:
bash -c 'trap "echo Ouch" INT; sh -c "trap \"\" INT; sleep 3"'
Run Code Online (Sandbox Code Playgroud)
bash
正在等待sh
(以及sleep
)忽略 SIGINT(因为trap "" INT
),因此 SIGINT 不会杀死sleep
或sh
. bash
不会忽略 SIGINT,但它的处理被推迟到sh
返回。您看到Ouch
正在显示,而不是在 上Ctrl+C,而是在sleep
和sh
正常终止之后。
请注意,该trap
命令为其运行的同一个 shell 设置了一个信号陷阱。所以当trap
命令在非交互式 shell 和父 shell 之外执行时,
Run Code Online (Sandbox Code Playgroud)$ trap "echo You hit control-C!" INT $ bash -c 'sleep 10; echo "$?"' ^C $
noninteractivebash
和sleep
命令不会trap
从父 shell继承它。信号处理程序在执行不同的命令时丢失(execve()
擦除进程的整个地址空间,包括处理程序的代码)。在 上execve()
,定义了处理程序的信号恢复为默认处理,那些被忽略的信号仍然被忽略。除此之外,在大多数 shell 中,trap
s 也在子 shell 中被重置。
当 Bash 通过 wait 内置函数等待异步命令时,接收到设置了陷阱的信号将导致 wait 内置函数立即返回,退出状态大于 128,在此之后陷阱将立即执行。
当wait
显式使用时, wait
会被任何设置了陷阱的信号(显然还有那些完全杀死外壳的信号)中断。
这使得在有陷阱信号时很难可靠地获得命令的退出状态:
$ bash -c 'trap "echo Ouch" INT; sh -c "trap \"\" INT; sleep 10" & wait "$!"; echo "$?"'
^COuch
130
Run Code Online (Sandbox Code Playgroud)
在那种情况下,sleep
并sh
没有被 SIGINT 杀死(因为他们忽略了它)。仍然wait
返回130
退出状态,因为在等待 时收到信号 (SIGINT) sh
。您需要重复wait "$!"
直到sh
真正终止才能获得sh
退出状态。