我有一个bash脚本,其内容是:
function foo {
echo "Foo!"
}
function clean {
echo "exiting"
}
trap clean EXIT
trap foo SIGTERM
echo "Starting process with PID: $$"
while :
do
sleep 60
done
我在终端上执行此操作:
./my_script
然后在另一个终端上执行此操作
kill -SIGTERM my_script_pid # obviously the PID is the one echoed from my_script
我希望看到消息“Foo!”从另一个终端,但它没有工作。 SIGKILL工作,EXIT代码也执行。
在终端上使用Ctrl-C my_script通常在触发器foo上运行,但不知怎的,我无法将SIGTERM信号从另一个终端发送到此终端。
用任何其他信号替换SIGTERM不会改变任何事情(除了Ctrl-C没有触发任何东西,它实际上在开始时映射到SIGUSR1。)
值得一提的是,被捕获的信号无效,任何其他信号都有默认行为。
那么,我错过了什么?有线索吗?
编辑:我刚刚检查过它不是特权问题(因为我能够发送SIGKILL,这很奇怪),但似乎并不是这样。答案 0 :(得分:4)
Bash仅在sleep
返回后运行陷阱。
要理解为什么,请考虑在C / Unix内部:当信号立即发送到bash时,bash设置的相应信号处理程序只执行类似received_sigterm = true
的操作。
只有当sleep
返回时,并且在启动wait
进程后发出的bash发出的sleep
系统调用也会返回,bash将恢复其正常工作并执行陷阱(注意到{{ 1}})。
这样做是有充分理由的:直接从信号处理程序执行I / O(或通常调用内核)通常会导致未定义的行为,据我所知 - 虽然我不能告诉更多相关信息。
除了这个技术原因之外,bash还没有立即运行陷阱的另一个原因是:这实际上会破坏shell的基本语义。作业(包括管道)严格按顺序执行,除非您明确搞乱后台作业。
答案 1 :(得分:1)
您最初打印的PID是针对执行脚本的bash
实例,而不是针对正在等待的sleep
进程。在sleep
期间,信号可能会被忽略。
如果您想查看所需的效果,请将sleep
替换为像ps
这样短暂的过程。
function foo {
echo "Foo!"
}
function clean {
echo "exiting"
}
trap clean EXIT
trap foo SIGTERM
echo "Starting process with PID: $$"
while :
do
ps > /dev/null
done