我需要在它们之间运行后台和管道数据中的几个子进程。当脚本退出时,我想杀死剩下的任何一个,所以我添加了
trap cleanup EXIT
cleanup()
{
echo "Cleaning up!"
pkill -TERM -P $$
}
由于我需要做出反应,如果其中一个进程报告错误,我创建了包装器函数。以fd
结尾的任何内容都是先前打开的文件描述符,连接到FIFO管道。
run_gui()
{
"$GAME_BIN" $args <&$gui_infd >&$gui_outfd # redirecting IO to some file descriptors
if [[ $? == 0 ]]; then
echo exiting ok
exit $OK_EXITCODE
else
exit $ERROR_EXITCODE
fi
}
函数run_ai1(), run_ai2
是类似的。
run_ai1()
{
"$ai1" <&$ai1_infd >&$ai1_outfd
if [[ $? == 0 || $? == 1 || $? == 2 ]]; then
exit $OK_EXITCODE
else
exit $ERROR_EXITCODE
fi
}
run_ai2()
{
"$ai2" <&$ai2_infd >&$ai2_outfd
if [[ $? == 0 || $? == 1 || $? == 2 ]]; then
exit $OK_EXITCODE
else
exit $ERROR_EXITCODE
fi
}
然后我运行这些功能并进行所需的管道
printinit 1 >&$ai1_infd
printinit 2 >&$ai2_infd
run_gui &
run_ai1 &
run_ai2 &
while true; do
echo "Started the loop"
while true; do
read -u $ai1_outfd line || echo "Nothing read"
echo $line
if [[ $line ]]; then
echo "$line" >&$gui_infd
echo "$line" >&$ai2_infd
if [[ "$line" == "END_TURN" ]]; then
break
fi
fi
done
sleep $turndelay
while true; do
read -u $ai2_outfd line || echo "nothing read"
echo $line
if [[ $line ]]; then
echo "$line" >&$gui_infd
echo "$line" >&$ai1_infd
if [[ "$line" == "END_TURN" ]]; then
break
fi
fi
done
sleep $turndelay
done
当$GAME_BIN
退出时,即关闭按钮关闭GUI时,我可以在stdout上看到exiting ok
消息,但根本没有调用cleanup
函数。当我在调用cleanup
之前向exit $OK_EXITCODE
添加手动调用时,虽然进程被终止:
./game.sh: line 309: 9193 Terminated run_gui
./game.sh: line 309: 9194 Terminated run_ai1
./game.sh: line 309: 9195 Terminated run_ai2
./game.sh: line 309: 9203 Terminated sleep $turndelay
循环仍在运行,脚本不会退出,因为它应该(exit $OK_EXITCODE
)。 AI脚本很简单:
#!/bin/sh
while true; do
echo END_TURN
done
我的脚本中没有任何wait
调用。我做错了什么?
有趣的是:当我在jobs -p
之后立即致电run_ai2 &
时,我会列出3个pid。另一方面,当我从cleanup
函数调用此命令时 - 输出为空。
此外,为什么sleep $turndelay
进程终止了?它不是一个儿童调用过程。
答案 0 :(得分:1)
我猜你正在通过子进程启动一些子进程。这样做:在另一个窗口中执行ps -ft pts/1
或任何你的tty。验证
同时将pkill更改为kill $(jobs -p)
并查看是否有效。
答案 1 :(得分:1)
陷阱脚本退出时会触发EXIT
陷阱。您的顶级脚本不会退出此处。
trap
不会被子shell继承,你的run_*
函数正在运行(从后台运行)所以它永远不会在子shell&#39时触发退出。
你想要的最有可能是你手动做的(虽然听起来有点不正确)。
当cleanup
退出时,您希望从run_gui
调用$GAME
函数。这样的事情。
run_gui() {
"$GAME_BIN" $args <&$gui_infd >&$gui_outfd # redirecting IO to some file descriptors
ret=$?
cleanup
exit $ret
}
然后,您只需要确保cleanup
获得正确的$$
值(在bash
中,对于您的使用,即使是在shell中$$
以来的shell是父进程ID,但您可能希望通过在主脚本中为信号设置处理程序并在run_gui
终止时发出主脚本信号来使其更明确来代替。)