如何实现终端关闭时自动终止其调用的后台Node.js服务器进程
解决关闭终端时同步终止Node.js服务器的问题
这问题我之前折腾过好几次,核心原因就是你用&把Node进程丢到后台后,它脱离了当前shell的进程组——终端关闭时会给当前shell发送SIGHUP终止信号,但这个信号不会传递到后台的Node进程,所以它就留在后台占着资源。下面给你两个实用的解决方案:
方案一:用trap捕获信号,主动清理进程
这个方法最稳妥,我们在脚本里记录Node进程的PID,然后设置信号监听,不管是终端关闭、按Ctrl+C中断,还是脚本正常退出,都自动杀死对应的Node进程。修改后的脚本如下:
#!/usr/bin/bash NODE_PATH="/home/linuxbrew/.linuxbrew/bin/node" FOUNDRY_PATH="/home/user/Games/foundryvtt/foundryvtt/main.js" FOUNDRY_DATA="/home/user/Games/foundryvtt/foundrydata" FOUNDRY_URL="127.0.0.1:30000" # 定义清理函数:负责终止Node进程 cleanup() { if [ -n "$NODE_PID" ]; then kill "$NODE_PID" 2>/dev/null echo "✅ Servidor FoundryVTT已终止" fi } # 设置信号捕获:终端关闭(SIGHUP)、Ctrl+C中断(SIGINT)、脚本退出(EXIT)时执行清理 trap cleanup SIGHUP SIGINT EXIT echo "🔧 Iniciando servidor FoundryVTT..." "$NODE_PATH" "$FOUNDRY_PATH" --dataPath="$FOUNDRY_DATA" & NODE_PID=$! # 记录最后一个后台进程的PID(也就是Node服务器的PID) echo "🌐 Abrindo localhost no browser..." sleep 2s xdg-open "$FOUNDRY_URL" $SHELL #TODO: code to run ngrok
关键说明:
NODE_PID=$!:$!是bash的内置变量,专门用来获取最后一个后台运行进程的PID,刚好能拿到我们启动的Node服务器ID。trap cleanup SIGHUP SIGINT EXIT:告诉脚本,只要收到这三种信号中的任意一种,就立刻执行cleanup函数。2>/dev/null:用来忽略“进程已经不存在”的错误提示,避免脚本退出时出现冗余报错。
方案二:让Node进程留在当前进程组
另一种思路是不让Node进程完全脱离当前shell的控制,这样终端关闭时的SIGHUP信号会传递给整个进程组,Node进程会跟着终止。你可以试试这种简化写法:
#!/usr/bin/bash NODE_PATH="/home/linuxbrew/.linuxbrew/bin/node" FOUNDRY_PATH="/home/user/Games/foundryvtt/foundryvtt/main.js" FOUNDRY_DATA="/home/user/Games/foundryvtt/foundrydata" FOUNDRY_URL="127.0.0.1:30000" echo "🔧 Iniciando servidor FoundryVTT..." # 启动进程并放到后台(保持在当前进程组) "$NODE_PATH" "$FOUNDRY_PATH" --dataPath="$FOUNDRY_DATA" & NODE_PID=$! echo "🌐 Abrindo localhost no browser..." sleep 2s xdg-open "$FOUNDRY_URL" # 等待用户退出当前shell后,自动终止Node进程 wait $NODE_PID
不过这个方案的可靠性不如方案一,因为如果用户在新shell里做了其他操作,可能会影响信号传递。
额外小技巧:清理残留进程
如果之前已经有残留的Node进程占着30000端口,可以用这几个命令快速清理:
# 找到占用30000端口的进程 lsof -i :30000 # 或者直接查找所有Node进程 ps aux | grep node # 杀死进程(替换成你查到的PID) kill <PID> # 如果进程不响应,强制杀死 kill -9 <PID>
内容的提问来源于stack exchange,提问作者DATA
相关产品推荐
相关产品推荐

