修改
This commit is contained in:
@@ -196,29 +196,58 @@ else
|
||||
command -v screen || { echo "❌ Screen安装失败"; exit 1; }
|
||||
fi
|
||||
|
||||
#!/bin/bash
|
||||
# ===== 优雅终止logic会话(先等内部程序退出 → 再退screen)=====
|
||||
echo "===== 优雅终止logic会话 ====="
|
||||
SCREEN_PID=""
|
||||
# 你实际使用的screen名称(从日志看是logic)
|
||||
SCREEN_NAME="%s{screen_name}"
|
||||
# 替换为你实际的screen名称(示例:logic)
|
||||
SCREEN_NAME="%s{screen_name}"
|
||||
# 日志文件路径(可根据需要调整)
|
||||
LOG_FILE="./screen_logic_exit.log"
|
||||
|
||||
# 调试开关(如需详细日志,取消set -x注释)
|
||||
set -o pipefail
|
||||
export PS4='[DEBUG] ${BASH_SOURCE}:${LINENO} - ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
|
||||
# set -x
|
||||
|
||||
# 定义:检查PID是否存活的函数
|
||||
# ========== 核心函数:基于kill -0的等待进程退出函数 ==========
|
||||
# 等待进程结束(带60秒超时,用kill -0检测存活,输出进度点)
|
||||
wait_for_process_exit() {
|
||||
local pidKilled=$1
|
||||
local begin=$(date +%s) # 记录开始时间(秒)
|
||||
local end
|
||||
local timeout=60 # 最大等待时间(秒)
|
||||
|
||||
# 循环检测进程是否存活
|
||||
while kill -0 $pidKilled > /dev/null 2>&1; do
|
||||
echo -n "." # 输出进度点,直观显示等待过程
|
||||
sleep 1;
|
||||
|
||||
# 检查是否超时
|
||||
end=$(date +%s)
|
||||
if [ $((end - begin)) -gt $timeout ]; then
|
||||
echo -e "\n⚠️ 等待进程$pidKilled退出超时(已等${timeout}秒)"
|
||||
break;
|
||||
fi
|
||||
done
|
||||
|
||||
# 最终状态提示
|
||||
if ! kill -0 $pidKilled > /dev/null 2>&1; then
|
||||
echo -e "\n✅ 进程$pidKilled已退出"
|
||||
fi
|
||||
}
|
||||
|
||||
# 定义:检查PID是否存活的函数(复用kill -0逻辑)
|
||||
pid_is_alive() {
|
||||
local pid=$1
|
||||
# 仅检查PID是否存在,不发送信号(最安全的方式)
|
||||
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
|
||||
if [ -n "$pid" ] && kill -0 "$pid" > /dev/null 2>&1; then
|
||||
return 0 # PID存活
|
||||
else
|
||||
return 1 # PID不存在
|
||||
fi
|
||||
}
|
||||
|
||||
# 定义安全的进程检测函数(带超时防卡)
|
||||
# 定义:获取screen会话下的所有子进程PID
|
||||
get_inner_procs() {
|
||||
local screen_pid=$1
|
||||
# 5秒超时,避免pstree卡住
|
||||
@@ -230,113 +259,90 @@ get_inner_procs() {
|
||||
echo "$procs"
|
||||
}
|
||||
|
||||
# 1. 检查logic会话是否存在
|
||||
if screen -ls "$SCREEN_NAME" 2>/dev/null | grep -q -E "[0-9]+\.$SCREEN_NAME"; then
|
||||
echo "找到$SCREEN_NAME会话,提取主PID..."
|
||||
SCREEN_PID=$(screen -ls "$SCREEN_NAME" | grep -oE '[0-9]+\.'"$SCREEN_NAME" | head -1 | cut -d. -f1)
|
||||
|
||||
if [ -z "$SCREEN_PID" ]; then
|
||||
echo "⚠️ 提取$SCREEN_NAME会话PID失败"
|
||||
else
|
||||
echo "✅ 提取到$SCREEN_NAME会话主PID:$SCREEN_PID"
|
||||
|
||||
# ========== 步骤1:给screen内所有子进程发优雅退出信号 ==========
|
||||
echo "给$SCREEN_NAME内所有程序发送优雅退出信号(SIGTERM)..."
|
||||
INNER_ALL_PROCS=$(get_inner_procs "$SCREEN_PID")
|
||||
|
||||
if [ -n "$INNER_ALL_PROCS" ]; then
|
||||
echo "📌 检测到screen内进程列表:$INNER_ALL_PROCS"
|
||||
for pid in $INNER_ALL_PROCS; do
|
||||
# 发送信号前检查子进程是否存活
|
||||
if pid_is_alive "$pid"; then
|
||||
if kill -15 "$pid" 2>/dev/null; then
|
||||
echo "✅ 已给进程$pid发送SIGTERM信号"
|
||||
else
|
||||
echo "⚠️ 进程$pid发送信号失败"
|
||||
fi
|
||||
else
|
||||
echo "ℹ️ 进程$pid已不存在,跳过发送信号"
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo "ℹ️ 未检测到$SCREEN_NAME内的子进程"
|
||||
fi
|
||||
# 第一步:提取screen主PID并校验
|
||||
SCREEN_PID=$(screen -ls "$SCREEN_NAME" | grep -oE '[0-9]+\.'"$SCREEN_NAME" | head -1 | cut -d. -f1)
|
||||
if [ -z "$SCREEN_PID" ]; then
|
||||
echo "ℹ️ 未找到$SCREEN_NAME会话对应的PID,跳过终止流程"
|
||||
# 去掉exit 1,直接跳过后续逻辑,不影响其他脚本执行
|
||||
else
|
||||
# ========== 仅当找到PID时,才执行以下终止流程 ==========
|
||||
echo "✅ 找到$SCREEN_NAME,主PID:$SCREEN_PID"
|
||||
|
||||
# 兜底:给screen内Shell发送exit指令
|
||||
echo "给screen内Shell发送exit指令(兜底)..."
|
||||
screen -S "$SCREEN_NAME" -p 0 -X stuff $'exit\n' 2>/dev/null
|
||||
sleep 1
|
||||
|
||||
# ========== 步骤2:循环等待内部程序退出(防卡优化) ==========
|
||||
echo "开始循环等待$SCREEN_NAME内部所有程序退出(最大60秒)..."
|
||||
WAIT_COUNT=0
|
||||
MAX_WAIT_SECONDS=60
|
||||
INNER_PROC_EXIST=true
|
||||
|
||||
while [ "$INNER_PROC_EXIST" = true ] && [ $WAIT_COUNT -lt $MAX_WAIT_SECONDS ]; do
|
||||
INNER_PROCS=$(get_inner_procs "$SCREEN_PID")
|
||||
|
||||
# 强制退出条件:即使进程检测失败也不卡住
|
||||
if [ -z "$INNER_PROCS" ]; then
|
||||
INNER_PROC_EXIST=false
|
||||
echo "✅ $SCREEN_NAME内部所有程序已退出(或进程检测完成)"
|
||||
else
|
||||
sleep 1
|
||||
WAIT_COUNT=$((WAIT_COUNT + 1))
|
||||
|
||||
# 每5秒输出状态
|
||||
if [ $((WAIT_COUNT % 5)) -eq 0 ]; then
|
||||
ELAPSED=$WAIT_COUNT
|
||||
echo "⏳ 等待中...残留进程:$INNER_PROCS(已等$ELAPSED秒,剩余$((MAX_WAIT_SECONDS - ELAPSED))秒)"
|
||||
# 第二步:导出【退出前】的实时日志(终端+文件双输出)
|
||||
echo -e "\n===== 【退出前】$SCREEN_NAME 内程序实时log ====="
|
||||
screen -S "$SCREEN_NAME" -p 0 -X hardcopy -h "$LOG_FILE" 2>/dev/null
|
||||
cat "$LOG_FILE"
|
||||
|
||||
# 第三步:给所有子进程发SIGTERM信号,并等待进程退出
|
||||
echo -e "\n===== 开始给所有子进程发优雅退出信号(SIGTERM) ====="
|
||||
INNER_PROCS=$(get_inner_procs "$SCREEN_PID")
|
||||
if [ -z "$INNER_PROCS" ]; then
|
||||
echo "ℹ 未检测到$SCREEN_NAME下的子进程"
|
||||
else
|
||||
echo "待处理子进程:$INNER_PROCS"
|
||||
for pid in $INNER_PROCS; do
|
||||
if pid_is_alive "$pid"; then
|
||||
echo -n "📌 终止进程$pid并等待退出:"
|
||||
kill -15 "$pid" # 发送SIGTERM优雅退出信号
|
||||
if [ $? -eq 0 ]; then
|
||||
wait_for_process_exit "$pid" # 调用等待函数
|
||||
else
|
||||
echo "❌ 进程$pid:发送SIGTERM失败"
|
||||
fi
|
||||
else
|
||||
echo "ℹ 进程$pid:已不存在,跳过"
|
||||
fi
|
||||
done
|
||||
|
||||
# 超时提示
|
||||
if [ "$INNER_PROC_EXIST" = true ]; then
|
||||
echo "⚠️ 等待超时(60秒),$SCREEN_NAME内部程序仍未退出"
|
||||
echo "📌 残留进程PID:$INNER_PROCS"
|
||||
fi
|
||||
|
||||
# ========== 步骤3:退出screen会话(核心修复:避免kill卡住) ==========
|
||||
if [ "$INNER_PROC_EXIST" = false ]; then
|
||||
echo "内部程序已退出,开始退出$SCREEN_NAME会话..."
|
||||
# 第一步:尝试正常退出screen会话
|
||||
if screen -S "$SCREEN_NAME" -X quit 2>/dev/null; then
|
||||
echo "✅ $SCREEN_NAME会话已通过screen -X quit退出"
|
||||
else
|
||||
echo "ℹ️ screen -X quit执行失败(会话可能已消失),检查PID是否存活..."
|
||||
# 第二步:仅当PID存活时,才执行kill,且加超时
|
||||
if pid_is_alive "$SCREEN_PID"; then
|
||||
echo "📌 PID $SCREEN_PID 仍存活,尝试kill终止(5秒超时)..."
|
||||
timeout 5 kill -15 "$SCREEN_PID" 2>/dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ 已给screen主进程$SCREEN_PID发送SIGTERM信号"
|
||||
else
|
||||
echo "⚠️ kill $SCREEN_PID 失败(超时/进程不存在)"
|
||||
fi
|
||||
else
|
||||
echo "ℹ️ PID $SCREEN_PID 已不存在,跳过kill操作"
|
||||
fi
|
||||
fi
|
||||
sleep 2
|
||||
else
|
||||
echo "⚠️ 内部程序未完全退出,跳过退出screen会话"
|
||||
fi
|
||||
|
||||
# 最终验证
|
||||
if screen -ls "$SCREEN_NAME" 2>/dev/null | grep -q -E "[0-9]+\.$SCREEN_NAME"; then
|
||||
echo "❌ $SCREEN_NAME会话最终仍未退出"
|
||||
else
|
||||
echo "✅ $SCREEN_NAME会话已完全退出"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "=== 未找到$SCREEN_NAME会话,跳过终止 ==="
|
||||
|
||||
# 验证子进程是否全部退出
|
||||
echo -e "\n===== 验证子进程退出状态 ====="
|
||||
REMAIN_PROCS=$(get_inner_procs "$SCREEN_PID")
|
||||
INNER_PROC_EXIST=true
|
||||
if [ -z "$REMAIN_PROCS" ]; then
|
||||
INNER_PROC_EXIST=false
|
||||
echo "✅ $SCREEN_NAME内部所有程序已退出"
|
||||
else
|
||||
echo "⚠️ 仍有残留进程:$REMAIN_PROCS"
|
||||
fi
|
||||
|
||||
# 退出screen会话
|
||||
if [ "$INNER_PROC_EXIST" = false ]; then
|
||||
echo "内部程序已退出,开始退出$SCREEN_NAME会话..."
|
||||
# 尝试正常退出screen会话
|
||||
if screen -S "$SCREEN_NAME" -X quit 2>/dev/null; then
|
||||
echo "✅ $SCREEN_NAME会话已通过screen -X quit退出"
|
||||
else
|
||||
echo "ℹ️ screen -X quit执行失败,检查PID是否存活..."
|
||||
# 仅当PID存活时,执行kill
|
||||
if pid_is_alive "$SCREEN_PID"; then
|
||||
echo "📌 PID $SCREEN_PID 仍存活,尝试kill终止..."
|
||||
timeout 5 kill -15 "$SCREEN_PID" 2>/dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ 已给screen主进程$SCREEN_PID发送SIGTERM信号"
|
||||
else
|
||||
echo "⚠️ kill $SCREEN_PID 失败"
|
||||
fi
|
||||
else
|
||||
echo "ℹ️ PID $SCREEN_PID 已不存在,跳过kill操作"
|
||||
fi
|
||||
fi
|
||||
sleep 2
|
||||
else
|
||||
echo "⚠️ 内部程序未完全退出,跳过退出screen会话"
|
||||
fi
|
||||
|
||||
# 最终验证
|
||||
echo -e "\n===== 最终验证 ====="
|
||||
if screen -ls "$SCREEN_NAME" 2>/dev/null | grep -q -E "[0-9]+\.$SCREEN_NAME"; then
|
||||
echo "❌ $SCREEN_NAME会话最终仍未退出"
|
||||
else
|
||||
echo "✅ $SCREEN_NAME会话已完全退出"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 关闭调试
|
||||
# set +x
|
||||
# 后续脚本可以从这里继续执行,不受上述逻辑影响
|
||||
echo -e "\n===== 终止logic会话流程结束,继续执行后续脚本 ====="
|
||||
|
||||
# ===== 准备下载目录 =====
|
||||
echo "创建工作目录:%s{work_dir}"
|
||||
|
||||
Reference in New Issue
Block a user