Docker容器中JAR运行脚本无法捕获错误退出码问题
解决Docker容器中JAR启动脚本的退出码检测失效问题
问题背景
我在Docker容器中实现了一个JAR运行脚本,功能是读取Docker卷指定目录下的所有JAR文件,依次以后台进程启动。需求是如果JAR文件启动失败,要在控制台显示错误并记录退出码。
原脚本(run-jars.sh)
number_of_jars=$(find "$JAR_HOME" -maxdepth 1 -type f -name "*.jar" | wc -l) printf "%s | [DEBUG] JAR files in the %s directory: %s\n" "$(date +"%Y-%b-%d %H:%M:%S")" "$JAR_HOME" "$number_of_jars" for jar_file in "$JAR_HOME"/*.jar; do printf "%s | [INFO] starting %s at the background...\n" "$(date +"%Y-%b-%d %H:%M:%S")" "$jar_file" nohup java -jar "$jar_file" 2>&1 & exit_code=$? pid=$! printf "%s | [DEBUG] PID of the %s is %s\n" "$(date +"%Y-%b-%d %H:%M:%S")" "$jar_file" "$pid" printf "%s | [DEBUG] Exit code of the %s is %s\n" "$(date +"%Y-%b-%d %H:%M:%S")" "$jar_file" "$exit_code" timeout 5 tail --pid=$pid -f /dev/null exit_code=$? printf "%s | [DEBUG] Exit code of the %s is %s\n" "$(date +"%Y-%b-%d %H:%M:%S")" "$jar_file" "$exit_code" if [ "$exit_code" -ne 0 ]; then printf "%s | [ERROR] Java application %s exited with an error: %s" "$(date +"%Y-%b-%d %H:%M:%S")" "$jar_file" "$exit_code" fi done
当前问题
脚本可正常启动JAR文件,但错误退出码检测失效:即使使用损坏的JAR文件,获取到的退出码始终为0。运行日志如下:
2024-Mar-03 13:38:24 | [DEBUG] JAR files in the /jars-to-run directory: 2 2024-Mar-03 13:38:24 | [INFO] running /jars-to-run/aa.jar at the background... Error: Invalid or corrupt jarfile /jars-to-run/aa.jar 2024-Mar-03 13:38:24 | [DEBUG] PID of the /jars-to-run/aa.jar is 139 2024-Mar-03 13:38:24 | [DEBUG] Exit code of the /jars-to-run/aa.jar is 0 2024-Mar-03 13:38:24 | [DEBUG] Exit code of the /jars-to-run/aa.jar is 0 2024-Mar-03 13:38:24 | [INFO] running /jars-to-run/rest-template-0.0.2.jar at the background... 2024-Mar-03 13:38:24 | [DEBUG] PID of the /jars-to-run/rest-template-0.0.2.jar is 146 2024-Mar-03 13:38:24 | [DEBUG] Exit code of the /jars-to-run/rest-template-0.0.2.jar is 0
问题根源
nohup的退出码干扰:nohup java -jar ... &执行后,$?获取的是nohup命令本身的退出码,而非Java进程的。只要nohup成功启动,就返回0,完全无法捕获Java启动失败的错误。timeout的退出码不反映实际进程状态:timeout 5 tail --pid=$pid -f /dev/null的退出码是timeout自身的:5秒内进程未退出则返回124,进程退出则返回0,和Java进程的实际退出码无关。
修复后的脚本
number_of_jars=$(find "$JAR_HOME" -maxdepth 1 -type f -name "*.jar" | wc -l) printf "%s | [DEBUG] JAR files in the %s directory: %s\n" "$(date +"%Y-%b-%d %H:%M:%S")" "$JAR_HOME" "$number_of_jars" for jar_file in "$JAR_HOME"/*.jar; do # 跳过非文件(避免无匹配jar时通配符被当作文件名) [ -f "$jar_file" ] || continue printf "%s | [INFO] starting %s at the background...\n" "$(date +"%Y-%b-%d %H:%M:%S")" "$jar_file" # 启动Java进程,输出定向到单独的nohup日志 nohup java -jar "$jar_file" > "${jar_file}.nohup.out" 2>&1 & pid=$! # 等待进程退出或超时5秒,获取真实退出码 (wait "$pid"; echo $?) > "${jar_file}.exitcode" & sleep 5 exit_code=$(cat "${jar_file}.exitcode" 2>/dev/null || echo 124) rm -f "${jar_file}.exitcode" printf "%s | [DEBUG] PID of the %s is %s\n" "$(date +"%Y-%b-%d %H:%M:%S")" "$jar_file" "$pid" printf "%s | [DEBUG] Exit code of the %s is %s\n" "$(date +"%Y-%b-%d %H:%M:%S")" "$jar_file" "$exit_code" if [ "$exit_code" -ne 0 ] && [ "$exit_code" -ne 124 ]; then printf "%s | [ERROR] Java application %s exited with an error: %s\n" "$(date +"%Y-%b-%d %H:%M:%S")" "$jar_file" "$exit_code" # 可选:打印错误日志内容 # printf "%s | [ERROR] Error details:\n" "$(date +"%Y-%b-%d %H:%M:%S")" # cat "${jar_file}.nohup.out" elif [ "$exit_code" -eq 124 ]; then printf "%s | [INFO] Java application %s is running in background (timeout reached)\n" "$(date +"%Y-%b-%d %H:%M:%S")" "$jar_file" fi done
修复说明
- 捕获真实退出码:通过后台
wait进程获取Java进程的退出码并写入临时文件,等待5秒后读取该文件,既避免无限等待,又能拿到进程的真实退出状态。 - 单独日志文件:将每个JAR的输出定向到
${jar_file}.nohup.out,方便后续排查启动失败的具体原因。 - 处理无JAR场景:添加
[ -f "$jar_file" ] || continue,防止目录下没有JAR文件时,通配符被当作无效文件名处理。 - 区分超时和错误:退出码124表示进程在5秒内未退出(正常运行),其他非0值则标记为启动失败。
内容的提问来源于stack exchange,提问作者zappee
相关产品推荐
相关产品推荐

