如何在Bash脚本中检测手动关闭的终端并执行后续命令
问题分析
你之前的脚本失效原因有两点:
- 捕获的
gnome-terminal进程PID会在终端启动子进程后立即退出,导致wait提前触发后续命令 - 脚本中
wait "$SECOND_PID" "$FIRST_PID"里的FIRST_PID未定义,属于无效参数
下面提供两种可行的解决方案:
方案1:使用gnome-terminal --wait参数(推荐)
--wait参数会让gnome-terminal持续运行,直到终端内的命令执行完毕才退出,这样捕获的PID能准确对应终端的生命周期。
修改后的脚本
#!/bin/bash BASE_DIR="$(cd "$(dirname "$0")" && pwd)" FIRST_DIR="$BASE_DIR" SECOND_DIR="$BASE_DIR/dir1/" THIRD_DIR="$BASE_DIR/dir2/" # 启动第一个终端(无需等待) gnome-terminal --working-directory="$FIRST_DIR" -- bash -c " echo 'lspci pcie cards'; ./Delete_log_files.sh; lspci -n; ./Load_drivers.sh; exec bash;" sleep 1 # 启动第二个终端,启用--wait并后台运行 gnome-terminal --wait --working-directory="$SECOND_DIR" -- bash -c " echo 'run GDMS app'; make; sudo ./main; # 若需停止测试后仍保留终端,可添加exec bash;否则去掉该行,停止测试后终端自动关闭 " & SECOND_PID=$! sleep 1 # 启动第三个终端,逻辑同第二个 gnome-terminal --wait --working-directory="$THIRD_DIR" -- bash -c " echo 'run GDMS app'; make; sudo ./main; " & THIRD_PID=$! # 等待两个测试终端全部关闭 wait "$SECOND_PID" "$THIRD_PID" # 执行清理命令 ./Delete_log.sh
说明
- 如果希望用户按下
Ctrl+C停止./main后立即触发清理,就去掉终端脚本里的exec bash,此时./main终止后脚本执行完毕,终端自动退出 - 如果需要用户停止测试后仍能在终端内操作,直到手动关闭终端才触发清理,保留
exec bash即可
方案2:跟踪./main进程PID(兼容旧版gnome-terminal)
如果你的gnome-terminal不支持--wait参数,可通过临时文件记录./main的PID,主脚本循环检测进程状态,直到两个测试进程都终止。
修改后的脚本
#!/bin/bash BASE_DIR="$(cd "$(dirname "$0")" && pwd)" FIRST_DIR="$BASE_DIR" SECOND_DIR="$BASE_DIR/dir1/" THIRD_DIR="$BASE_DIR/dir2/" # 创建临时目录存储PID文件 TEMP_DIR=$(mktemp -d) SECOND_PID_FILE="$TEMP_DIR/second.pid" THIRD_PID_FILE="$TEMP_DIR/third.pid" # 启动第一个终端 gnome-terminal --working-directory="$FIRST_DIR" -- bash -c " echo 'lspci pcie cards'; ./Delete_log_files.sh; lspci -n; ./Load_drivers.sh; exec bash;" sleep 1 # 启动第二个终端,记录./main的PID并等待其终止 gnome-terminal --working-directory="$SECOND_DIR" -- bash -c " echo 'run GDMS app'; make; sudo ./main & MAIN_PID=\$! echo \$MAIN_PID > '$SECOND_PID_FILE' wait \$MAIN_PID rm -f '$SECOND_PID_FILE' exec bash;" & sleep 1 # 启动第三个终端,逻辑同第二个 gnome-terminal --working-directory="$THIRD_DIR" -- bash -c " echo 'run GDMS app'; make; sudo ./main & MAIN_PID=\$! echo \$MAIN_PID > '$THIRD_PID_FILE' wait \$MAIN_PID rm -f '$THIRD_PID_FILE' exec bash;" & # 循环检测两个测试进程是否仍在运行 while true; do SECOND_RUNNING=0 if [ -f "$SECOND_PID_FILE" ]; then SECOND_PID=$(cat "$SECOND_PID_FILE") # 检查PID是否存在 if kill -0 "$SECOND_PID" 2>/dev/null; then SECOND_RUNNING=1 else rm -f "$SECOND_PID_FILE" fi fi THIRD_RUNNING=0 if [ -f "$THIRD_PID_FILE" ]; then THIRD_PID=$(cat "$THIRD_PID_FILE") if kill -0 "$THIRD_PID" 2>/dev/null; then THIRD_RUNNING=1 else rm -f "$THIRD_PID_FILE" fi fi # 两个进程都终止则退出循环 if [ $SECOND_RUNNING -eq 0 ] && [ $THIRD_RUNNING -eq 0 ]; then break fi sleep 2 done # 清理临时目录 rm -rf "$TEMP_DIR" # 执行清理命令 ./Delete_log.sh
说明
- 该方案不依赖
gnome-terminal的特殊参数,兼容性更强 - 即使终端被意外关闭,主脚本也会检测到PID失效,自动终止循环并执行清理
内容的提问来源于stack exchange,提问作者Jonathan
相关产品推荐
相关产品推荐

