From 711d63ec0c56f3f860256a3d0b6165af67868ed5 Mon Sep 17 00:00:00 2001 From: huliyong <2783385703@qq.com> Date: Fri, 26 Sep 2025 23:10:38 +0800 Subject: [PATCH 1/6] =?UTF-8?q?docs:=20=E6=B7=BB=E5=8A=A0Java=E5=BA=94?= =?UTF-8?q?=E7=94=A8=E7=AE=A1=E7=90=86=E8=84=9A=E6=9C=AC=E5=8F=8A=E5=85=B6?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E8=AF=B4=E6=98=8E=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加了restart-java-app.sh脚本用于管理Java应用的启动、停止和重启 同时添加了详细的README文档说明脚本功能和使用方法 --- README-restart-script.md | 175 +++++++++++++++++++++++++++++++++ restart-java-app.sh | 247 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 422 insertions(+) create mode 100644 README-restart-script.md create mode 100644 restart-java-app.sh diff --git a/README-restart-script.md b/README-restart-script.md new file mode 100644 index 0000000..aa4e85e --- /dev/null +++ b/README-restart-script.md @@ -0,0 +1,175 @@ +# Java应用启动/重启脚本使用说明 + +## 概述 + +`restart-java-app.sh` 是一个用于在Linux系统下管理Java JAR包应用的Shell脚本。它可以自动检测应用是否运行,并提供启动、停止、重启等功能。 + +## 功能特性 + +- ✅ 自动检测应用运行状态 +- ✅ 优雅停止应用(先发送TERM信号,超时后强制停止) +- ✅ 智能端口检测,避免端口冲突 +- ✅ PID文件管理,确保进程跟踪准确 +- ✅ 彩色日志输出,便于查看状态 +- ✅ 详细的错误处理和状态反馈 +- ✅ 支持多种命令选项 + +## 配置参数 + +在使用脚本前,需要修改脚本开头的配置参数: + +```bash +# ======================================== +# 配置参数 - 请根据实际情况修改 +# ======================================== +JAR_NAME="your-app.jar" # JAR包名称 +PORT=8080 # 应用端口号 +``` + +### 主要配置项 + +| 参数 | 说明 | 示例 | +|------|------|------| +| `JAR_NAME` | JAR包文件名 | `"my-spring-boot-app.jar"` | +| `PORT` | 应用监听端口 | `8080` | + +### 可选配置项 + +| 参数 | 说明 | 默认值 | +|------|------|--------| +| `JAVA_OPTS` | JVM启动参数 | `"-Xms512m -Xmx1024m"` | +| `LOG_FILE` | 日志文件名 | `"app.log"` | +| `PID_FILE` | PID文件名 | `"app.pid"` | + +## 使用方法 + +### 1. 设置执行权限 + +```bash +chmod +x restart-java-app.sh +``` + +### 2. 基本用法 + +```bash +# 重启应用(默认操作) +./restart-java-app.sh + +# 或者明确指定重启 +./restart-java-app.sh restart +``` + +### 3. 其他命令 + +```bash +# 启动应用 +./restart-java-app.sh start + +# 停止应用 +./restart-java-app.sh stop + +# 查看应用状态 +./restart-java-app.sh status + +# 显示帮助信息 +./restart-java-app.sh help +``` + +## 命令详解 + +### start - 启动应用 +- 检查JAR文件是否存在 +- 检查端口是否被占用 +- 启动Java应用 +- 等待应用完全启动 +- 验证端口监听状态 + +### stop - 停止应用 +- 查找应用进程PID +- 发送TERM信号优雅停止 +- 等待最多30秒 +- 超时后强制停止(KILL信号) +- 清理PID文件 + +### restart - 重启应用 +- 先执行stop操作 +- 等待2秒 +- 再执行start操作 + +### status - 查看状态 +- 显示应用运行状态 +- 显示进程PID +- 显示端口监听状态 + +## 输出说明 + +脚本使用彩色输出来区分不同类型的消息: + +- 🔴 **红色**: 错误信息 +- 🟢 **绿色**: 成功信息 +- 🟡 **黄色**: 警告信息 +- 🔵 **蓝色**: 操作信息 + +## 文件说明 + +脚本运行时会创建以下文件: + +- `app.log`: 应用运行日志 +- `app.pid`: 应用进程PID文件 + +## 常见问题 + +### Q: 提示"JAR文件不存在" +**A**: 检查`JAR_NAME`配置是否正确,确保JAR文件在脚本同一目录下。 + +### Q: 提示"端口已被占用" +**A**: 检查是否有其他应用占用了配置的端口,或修改`PORT`配置。 + +### Q: 应用启动失败 +**A**: 查看`app.log`日志文件,检查具体错误原因。 + +### Q: 无法停止应用 +**A**: 脚本会先尝试优雅停止,30秒后自动强制停止。 + +## 系统要求 + +- Linux操作系统 +- Bash Shell +- Java运行环境 +- 网络工具(netstat、ss或lsof中的任意一个) + +## 注意事项 + +1. 确保脚本有执行权限 +2. 确保Java环境已正确安装 +3. 确保JAR文件路径正确 +4. 建议在生产环境使用前先在测试环境验证 +5. 定期检查日志文件大小,避免占用过多磁盘空间 + +## 示例 + +假设你有一个名为`my-app.jar`的Spring Boot应用,需要在8080端口运行: + +1. 修改脚本配置: +```bash +JAR_NAME="my-app.jar" +PORT=8080 +``` + +2. 启动应用: +```bash +./restart-java-app.sh start +``` + +3. 查看状态: +```bash +./restart-java-app.sh status +``` + +输出示例: +``` +[2024-01-15 10:30:15] 应用正在运行 +[2024-01-15 10:30:15] PID: 12345 +[2024-01-15 10:30:15] 端口: 8080 +[2024-01-15 10:30:15] 端口状态: 正在监听 +``` \ No newline at end of file diff --git a/restart-java-app.sh b/restart-java-app.sh new file mode 100644 index 0000000..a822616 --- /dev/null +++ b/restart-java-app.sh @@ -0,0 +1,247 @@ +#!/bin/bash + +# ======================================== +# Java应用启动/重启脚本 +# 使用方法: ./restart-java-app.sh +# ======================================== + +# ======================================== +# 配置参数 - 请根据实际情况修改 +# ======================================== +JAR_NAME="your-app.jar" # JAR包名称 +PORT=8080 # 应用端口号 + +# ======================================== +# 其他配置(一般不需要修改) +# ======================================== +JAVA_OPTS="-Xms512m -Xmx1024m" # JVM参数 +LOG_FILE="app.log" # 日志文件名 +PID_FILE="app.pid" # PID文件名 + +# 颜色输出 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# ======================================== +# 函数定义 +# ======================================== + +# 打印带颜色的消息 +print_message() { + local color=$1 + local message=$2 + echo -e "${color}[$(date '+%Y-%m-%d %H:%M:%S')] ${message}${NC}" +} + +# 检查JAR文件是否存在 +check_jar_exists() { + if [ ! -f "$JAR_NAME" ]; then + print_message $RED "错误: JAR文件 '$JAR_NAME' 不存在!" + print_message $YELLOW "请检查JAR_NAME配置是否正确,或确保JAR文件在当前目录下" + exit 1 + fi +} + +# 检查端口是否被占用 +check_port() { + local port=$1 + if command -v netstat >/dev/null 2>&1; then + netstat -tlnp 2>/dev/null | grep ":$port " >/dev/null + elif command -v ss >/dev/null 2>&1; then + ss -tlnp | grep ":$port " >/dev/null + else + # 如果没有netstat和ss,尝试使用lsof + if command -v lsof >/dev/null 2>&1; then + lsof -i :$port >/dev/null 2>&1 + else + return 1 + fi + fi +} + +# 获取运行中的应用PID +get_app_pid() { + if [ -f "$PID_FILE" ]; then + local pid=$(cat "$PID_FILE") + # 检查进程是否真的在运行 + if ps -p $pid > /dev/null 2>&1; then + echo $pid + else + # PID文件存在但进程不存在,删除PID文件 + rm -f "$PID_FILE" + echo "" + fi + else + # 通过进程名查找 + pgrep -f "$JAR_NAME" | head -1 + fi +} + +# 停止应用 +stop_app() { + local pid=$(get_app_pid) + + if [ -z "$pid" ]; then + print_message $YELLOW "应用未运行" + return 0 + fi + + print_message $YELLOW "正在停止应用 (PID: $pid)..." + + # 优雅停止 + kill $pid + + # 等待进程停止 + local count=0 + while [ $count -lt 30 ]; do + if ! ps -p $pid > /dev/null 2>&1; then + print_message $GREEN "应用已成功停止" + rm -f "$PID_FILE" + return 0 + fi + sleep 1 + count=$((count + 1)) + done + + # 如果优雅停止失败,强制停止 + print_message $YELLOW "优雅停止超时,强制停止应用..." + kill -9 $pid 2>/dev/null + + sleep 2 + if ! ps -p $pid > /dev/null 2>&1; then + print_message $GREEN "应用已强制停止" + rm -f "$PID_FILE" + return 0 + else + print_message $RED "停止应用失败" + return 1 + fi +} + +# 启动应用 +start_app() { + print_message $BLUE "正在启动应用..." + + # 检查端口是否被占用 + if check_port $PORT; then + print_message $RED "端口 $PORT 已被占用,请检查是否有其他应用在使用该端口" + return 1 + fi + + # 启动应用 + nohup java $JAVA_OPTS -jar "$JAR_NAME" --server.port=$PORT > "$LOG_FILE" 2>&1 & + local pid=$! + + # 保存PID + echo $pid > "$PID_FILE" + + # 等待应用启动 + print_message $BLUE "等待应用启动..." + sleep 3 + + # 检查进程是否还在运行 + if ps -p $pid > /dev/null 2>&1; then + # 检查端口是否监听 + local count=0 + while [ $count -lt 30 ]; do + if check_port $PORT; then + print_message $GREEN "应用启动成功!" + print_message $GREEN "PID: $pid" + print_message $GREEN "端口: $PORT" + print_message $GREEN "日志文件: $LOG_FILE" + return 0 + fi + sleep 1 + count=$((count + 1)) + done + + print_message $YELLOW "应用进程已启动,但端口 $PORT 未监听,请检查日志文件: $LOG_FILE" + return 1 + else + print_message $RED "应用启动失败,请检查日志文件: $LOG_FILE" + rm -f "$PID_FILE" + return 1 + fi +} + +# 显示应用状态 +show_status() { + local pid=$(get_app_pid) + + if [ -n "$pid" ]; then + print_message $GREEN "应用正在运行" + print_message $GREEN "PID: $pid" + print_message $GREEN "端口: $PORT" + + if check_port $PORT; then + print_message $GREEN "端口状态: 正在监听" + else + print_message $YELLOW "端口状态: 未监听" + fi + else + print_message $YELLOW "应用未运行" + fi +} + +# 显示帮助信息 +show_help() { + echo "Java应用管理脚本" + echo "" + echo "使用方法:" + echo " $0 [选项]" + echo "" + echo "选项:" + echo " start 启动应用" + echo " stop 停止应用" + echo " restart 重启应用" + echo " status 显示应用状态" + echo " help 显示帮助信息" + echo "" + echo "配置:" + echo " JAR包名称: $JAR_NAME" + echo " 端口号: $PORT" + echo "" + echo "注意: 请确保在脚本开头正确配置 JAR_NAME 和 PORT 参数" +} + +# ======================================== +# 主程序 +# ======================================== + +# 检查JAR文件是否存在 +check_jar_exists + +# 处理命令行参数 +case "${1:-restart}" in + "start") + start_app + ;; + "stop") + stop_app + ;; + "restart") + print_message $BLUE "重启应用..." + stop_app + if [ $? -eq 0 ]; then + sleep 2 + start_app + else + print_message $RED "停止应用失败,取消重启" + exit 1 + fi + ;; + "status") + show_status + ;; + "help"|"-h"|"--help") + show_help + ;; + *) + print_message $RED "未知选项: $1" + show_help + exit 1 + ;; +esac \ No newline at end of file From 137646397cc122e337e8dc52a06b1d6cc19baec5 Mon Sep 17 00:00:00 2001 From: huliyong <2783385703@qq.com> Date: Fri, 26 Sep 2025 23:19:07 +0800 Subject: [PATCH 2/6] =?UTF-8?q?feat(restart-script):=20=E6=B7=BB=E5=8A=A0J?= =?UTF-8?q?AR=E6=96=87=E4=BB=B6=E8=87=AA=E5=8A=A8=E6=A3=80=E6=B5=8B?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在重启脚本中添加自动检测JAR文件的功能: - 当JAR_NAME为空时自动检测当前目录下的JAR文件 - 如果只有一个JAR文件则自动使用 - 如果有多个JAR文件则提示用户指定 - 更新README文档说明新功能和使用示例 --- README-restart-script.md | 35 ++++++++++++++++++++++++++++++++--- restart-java-app.sh | 40 +++++++++++++++++++++++++++++++++------- 2 files changed, 65 insertions(+), 10 deletions(-) diff --git a/README-restart-script.md b/README-restart-script.md index aa4e85e..bb33016 100644 --- a/README-restart-script.md +++ b/README-restart-script.md @@ -22,7 +22,7 @@ # ======================================== # 配置参数 - 请根据实际情况修改 # ======================================== -JAR_NAME="your-app.jar" # JAR包名称 +JAR_NAME="" # JAR包名称(留空则自动检测) PORT=8080 # 应用端口号 ``` @@ -30,9 +30,14 @@ PORT=8080 # 应用端口号 | 参数 | 说明 | 示例 | |------|------|------| -| `JAR_NAME` | JAR包文件名 | `"my-spring-boot-app.jar"` | +| `JAR_NAME` | JAR包文件名(留空可自动检测) | `"my-spring-boot-app.jar"` 或 `""` | | `PORT` | 应用监听端口 | `8080` | +**自动检测功能**: 如果将 `JAR_NAME` 留空(`JAR_NAME=""`),脚本会自动检测当前目录下的JAR文件: +- 如果只有一个JAR文件,自动使用该文件 +- 如果有多个JAR文件,会提示用户指定具体的JAR文件名 +- 如果没有JAR文件,会提示错误 + ### 可选配置项 | 参数 | 说明 | 默认值 | @@ -120,7 +125,12 @@ chmod +x restart-java-app.sh ## 常见问题 ### Q: 提示"JAR文件不存在" -**A**: 检查`JAR_NAME`配置是否正确,确保JAR文件在脚本同一目录下。 +**A**: +- 如果使用自动检测模式(`JAR_NAME=""`),确保当前目录下有且仅有一个JAR文件 +- 如果指定了JAR文件名,检查`JAR_NAME`配置是否正确,确保JAR文件在脚本同一目录下 + +### Q: 提示"当前目录下有多个JAR文件" +**A**: 当目录下有多个JAR文件时,需要在脚本中明确指定`JAR_NAME`,例如:`JAR_NAME="my-app.jar"` ### Q: 提示"端口已被占用" **A**: 检查是否有其他应用占用了配置的端口,或修改`PORT`配置。 @@ -148,6 +158,25 @@ chmod +x restart-java-app.sh ## 示例 +### 示例1: 自动检测模式(推荐) + +如果你的目录下只有一个JAR文件,可以使用自动检测模式: + +1. 保持脚本配置为默认: +```bash +JAR_NAME="" # 留空,自动检测 +PORT=8080 +``` + +2. 启动应用: +```bash +./restart-java-app.sh start +``` + +脚本会自动检测并使用目录下唯一的JAR文件。 + +### 示例2: 指定JAR文件名 + 假设你有一个名为`my-app.jar`的Spring Boot应用,需要在8080端口运行: 1. 修改脚本配置: diff --git a/restart-java-app.sh b/restart-java-app.sh index a822616..60d0410 100644 --- a/restart-java-app.sh +++ b/restart-java-app.sh @@ -8,7 +8,7 @@ # ======================================== # 配置参数 - 请根据实际情况修改 # ======================================== -JAR_NAME="your-app.jar" # JAR包名称 +JAR_NAME="" # JAR包名称(留空则自动检测) PORT=8080 # 应用端口号 # ======================================== @@ -38,10 +38,36 @@ print_message() { # 检查JAR文件是否存在 check_jar_exists() { - if [ ! -f "$JAR_NAME" ]; then - print_message $RED "错误: JAR文件 '$JAR_NAME' 不存在!" - print_message $YELLOW "请检查JAR_NAME配置是否正确,或确保JAR文件在当前目录下" - exit 1 + # 如果JAR_NAME为空,自动检测当前目录下的JAR文件 + if [ -z "$JAR_NAME" ]; then + local jar_files=(*.jar) + local jar_count=${#jar_files[@]} + + # 检查是否存在JAR文件 + if [ ! -f "${jar_files[0]}" ]; then + print_message $RED "错误: 当前目录下没有找到JAR文件!" + print_message $YELLOW "请将JAR文件放在脚本同一目录下,或在脚本中指定JAR_NAME" + exit 1 + fi + + # 如果只有一个JAR文件,自动使用它 + if [ $jar_count -eq 1 ]; then + JAR_NAME="${jar_files[0]}" + print_message $BLUE "自动检测到JAR文件: $JAR_NAME" + else + print_message $RED "错误: 当前目录下有多个JAR文件,请在脚本中指定JAR_NAME:" + for jar in "${jar_files[@]}"; do + print_message $YELLOW " - $jar" + done + exit 1 + fi + else + # 检查指定的JAR文件是否存在 + if [ ! -f "$JAR_NAME" ]; then + print_message $RED "错误: JAR文件 '$JAR_NAME' 不存在!" + print_message $YELLOW "请检查JAR_NAME配置是否正确,或确保JAR文件在当前目录下" + exit 1 + fi fi } @@ -201,10 +227,10 @@ show_help() { echo " help 显示帮助信息" echo "" echo "配置:" - echo " JAR包名称: $JAR_NAME" + echo " JAR包名称: ${JAR_NAME:-"自动检测"}" echo " 端口号: $PORT" echo "" - echo "注意: 请确保在脚本开头正确配置 JAR_NAME 和 PORT 参数" + echo "注意: 如果目录下只有一个JAR文件,可以将JAR_NAME留空实现自动检测" } # ======================================== From 77013ac4b6af35c9f84f842a62d620dd65f82f89 Mon Sep 17 00:00:00 2001 From: huliyong <2783385703@qq.com> Date: Sat, 27 Sep 2025 00:47:08 +0800 Subject: [PATCH 3/6] =?UTF-8?q?feat(restart-script):=20=E5=A2=9E=E5=BC=BAJ?= =?UTF-8?q?ava=E5=BA=94=E7=94=A8=E7=AE=A1=E7=90=86=E8=84=9A=E6=9C=AC?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=B9=B6=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加多JAR包交互式选择和批量操作功能 - 实现JAR包昵称模糊匹配和'all'参数支持 - 改进日志文件命名规则,自动生成带时间戳的日志 - 增加输入验证和安全检查功能 - 优化状态显示和错误处理机制 - 更新README文档以反映新功能和使用方法 --- README-restart-script.md | 124 ++++-- restart-java-app.sh | 1062 ++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 974 insertions(+), 212 deletions(-) diff --git a/README-restart-script.md b/README-restart-script.md index bb33016..8db6a8d 100644 --- a/README-restart-script.md +++ b/README-restart-script.md @@ -1,18 +1,20 @@ -# Java应用启动/重启脚本使用说明 +# Java应用启动脚本使用说明 ## 概述 -`restart-java-app.sh` 是一个用于在Linux系统下管理Java JAR包应用的Shell脚本。它可以自动检测应用是否运行,并提供启动、停止、重启等功能。 +`restart-java-app.sh` 是一个功能强大的Java应用管理脚本,支持启动、停止、重启和状态查看等操作。脚本支持单个JAR包操作和多JAR包批量操作,提供灵活的JAR包昵称匹配功能。 -## 功能特性 +## 主要功能 -- ✅ 自动检测应用运行状态 -- ✅ 优雅停止应用(先发送TERM信号,超时后强制停止) -- ✅ 智能端口检测,避免端口冲突 -- ✅ PID文件管理,确保进程跟踪准确 -- ✅ 彩色日志输出,便于查看状态 -- ✅ 详细的错误处理和状态反馈 -- ✅ 支持多种命令选项 +- ✅ **自动JAR包检测** - 当目录中只有一个JAR包时自动选择 +- ✅ **多JAR包选择** - 当目录中有多个JAR包时提供交互式选择菜单 +- ✅ **多JAR包批量操作** - 支持通过昵称同时操作多个JAR包 +- ✅ **模糊匹配** - 支持通过昵称模糊匹配JAR文件名 +- ✅ **全量操作** - 支持使用'all'参数操作所有JAR包 +- ✅ **进程管理** - 自动管理应用进程的启动和停止 +- ✅ **日志管理** - 自动生成基于JAR包名和时间戳的日志文件 +- ✅ **状态监控** - 实时查看应用运行状态 +- ✅ **优雅停止** - 先尝试正常停止,必要时强制终止 ## 配置参数 @@ -23,7 +25,6 @@ # 配置参数 - 请根据实际情况修改 # ======================================== JAR_NAME="" # JAR包名称(留空则自动检测) -PORT=8080 # 应用端口号 ``` ### 主要配置项 @@ -31,11 +32,10 @@ PORT=8080 # 应用端口号 | 参数 | 说明 | 示例 | |------|------|------| | `JAR_NAME` | JAR包文件名(留空可自动检测) | `"my-spring-boot-app.jar"` 或 `""` | -| `PORT` | 应用监听端口 | `8080` | **自动检测功能**: 如果将 `JAR_NAME` 留空(`JAR_NAME=""`),脚本会自动检测当前目录下的JAR文件: - 如果只有一个JAR文件,自动使用该文件 -- 如果有多个JAR文件,会提示用户指定具体的JAR文件名 +- 如果有多个JAR文件,会显示选择菜单让用户选择具体的JAR文件 - 如果没有JAR文件,会提示错误 ### 可选配置项 @@ -43,7 +43,7 @@ PORT=8080 # 应用端口号 | 参数 | 说明 | 默认值 | |------|------|--------| | `JAVA_OPTS` | JVM启动参数 | `"-Xms512m -Xmx1024m"` | -| `LOG_FILE` | 日志文件名 | `"app.log"` | +| `LOG_FILE` | 日志文件名(留空则自动生成) | `""` | | `PID_FILE` | PID文件名 | `"app.pid"` | ## 使用方法 @@ -84,10 +84,9 @@ chmod +x restart-java-app.sh ### start - 启动应用 - 检查JAR文件是否存在 -- 检查端口是否被占用 - 启动Java应用 - 等待应用完全启动 -- 验证端口监听状态 +- 验证应用启动状态 ### stop - 停止应用 - 查找应用进程PID @@ -104,7 +103,6 @@ chmod +x restart-java-app.sh ### status - 查看状态 - 显示应用运行状态 - 显示进程PID -- 显示端口监听状态 ## 输出说明 @@ -119,8 +117,18 @@ chmod +x restart-java-app.sh 脚本运行时会创建以下文件: -- `app.log`: 应用运行日志 -- `app.pid`: 应用进程PID文件 +- **日志文件**: 自动生成,格式为 `{JAR包名}_{启动时间}.log`,例如:`my-app_20240115_143025.log` +- **PID文件**: `app.pid` - 应用进程PID文件 + +### 日志文件命名规则 + +- **自动生成模式**(推荐):当 `LOG_FILE=""` 时,系统会自动生成日志文件名 +- **格式**: `{JAR包名称}_{年月日_时分秒}.log` +- **示例**: + - JAR包:`my-spring-boot-app.jar` + - 启动时间:2024年1月15日 14:30:25 + - 日志文件:`my-spring-boot-app_20240115_143025.log` +- **手动指定**:如果在脚本中设置了 `LOG_FILE="custom.log"`,则使用指定的文件名 ## 常见问题 @@ -130,10 +138,7 @@ chmod +x restart-java-app.sh - 如果指定了JAR文件名,检查`JAR_NAME`配置是否正确,确保JAR文件在脚本同一目录下 ### Q: 提示"当前目录下有多个JAR文件" -**A**: 当目录下有多个JAR文件时,需要在脚本中明确指定`JAR_NAME`,例如:`JAR_NAME="my-app.jar"` - -### Q: 提示"端口已被占用" -**A**: 检查是否有其他应用占用了配置的端口,或修改`PORT`配置。 +**A**: 当目录下有多个JAR文件时,脚本会显示选择菜单,按提示输入对应的序号即可选择要启动的JAR文件。你也可以在脚本中明确指定`JAR_NAME`,例如:`JAR_NAME="my-app.jar"` ### Q: 应用启动失败 **A**: 查看`app.log`日志文件,检查具体错误原因。 @@ -146,7 +151,6 @@ chmod +x restart-java-app.sh - Linux操作系统 - Bash Shell - Java运行环境 -- 网络工具(netstat、ss或lsof中的任意一个) ## 注意事项 @@ -165,29 +169,38 @@ chmod +x restart-java-app.sh 1. 保持脚本配置为默认: ```bash JAR_NAME="" # 留空,自动检测 -PORT=8080 ``` 2. 启动应用: ```bash +# 启动应用 ./restart-java-app.sh start + +# 重启应用 +./restart-java-app.sh restart ``` 脚本会自动检测并使用目录下唯一的JAR文件。 ### 示例2: 指定JAR文件名 -假设你有一个名为`my-app.jar`的Spring Boot应用,需要在8080端口运行: +假设你有一个名为`my-app.jar`的Spring Boot应用: 1. 修改脚本配置: ```bash JAR_NAME="my-app.jar" -PORT=8080 ``` -2. 启动应用: +2. 使用不同方式启动: ```bash +# 启动应用 ./restart-java-app.sh start + +# 重启应用 +./restart-java-app.sh restart + +# 停止应用 +./restart-java-app.sh stop ``` 3. 查看状态: @@ -195,10 +208,57 @@ PORT=8080 ./restart-java-app.sh status ``` +### 示例3: 多JAR文件选择 + +当目录下有多个JAR文件时,脚本会提供交互式选择: + +```bash +# 启动脚本,会显示选择菜单 +./restart-java-app.sh start +``` + +## 交互式JAR包选择 + +当脚本检测到多个JAR文件时,会提供交互式选择界面: + +```bash +检测到多个JAR文件,请选择要启动的应用: + +[1] user-service-1.0.0.jar +[2] order-service-1.0.0.jar +[3] payment-service-1.0.0.jar + +输入选项: + - 单个序号: 1 (选择第1个JAR包) + - 多个序号: 1,3,5 或 1 3 5 (选择多个JAR包) + - 全部选择: all (选择所有JAR包) + +请输入选择: +``` + +**支持的输入格式:** +- `1` - 选择单个JAR包 +- `1,3,5` - 选择多个JAR包(逗号分隔) +- `1 3 5` - 选择多个JAR包(空格分隔) +- `all` - 选择所有JAR包 + +选择多个JAR包时,脚本会自动切换到批量操作模式。 + 输出示例: ``` -[2024-01-15 10:30:15] 应用正在运行 -[2024-01-15 10:30:15] PID: 12345 -[2024-01-15 10:30:15] 端口: 8080 -[2024-01-15 10:30:15] 端口状态: 正在监听 +检测到多个JAR文件,请选择要启动的应用: + +[1] my-web-app.jar +[2] my-api-service.jar +[3] my-batch-job.jar + +请输入序号 (1-3): 2 +✓ 已选择: my-api-service.jar + +正在启动应用... +JAR包: my-api-service.jar +JVM参数: -Xms512m -Xmx1024m +日志文件: my-api-service_20240115_143025.log +✓ 应用启动成功 (PID: 12345) +日志文件: my-api-service_20240115_143025.log ``` \ No newline at end of file diff --git a/restart-java-app.sh b/restart-java-app.sh index 60d0410..011aabb 100644 --- a/restart-java-app.sh +++ b/restart-java-app.sh @@ -5,35 +5,327 @@ # 使用方法: ./restart-java-app.sh # ======================================== -# ======================================== -# 配置参数 - 请根据实际情况修改 -# ======================================== -JAR_NAME="" # JAR包名称(留空则自动检测) -PORT=8080 # 应用端口号 +# 全局配置 +JAR_NAME="" # JAR文件名(为空时自动检测) +JAVA_OPTS="-Xms512m -Xmx1024m" # JVM参数 +LOG_FILE="" # 日志文件名(自动生成) -# ======================================== -# 其他配置(一般不需要修改) -# ======================================== -JAVA_OPTS="-Xms512m -Xmx1024m" # JVM参数 -LOG_FILE="app.log" # 日志文件名 -PID_FILE="app.pid" # PID文件名 +# 运行时配置常量 +STOP_TIMEOUT=15 # 停止超时时间(秒) +STARTUP_WAIT=3 # 启动等待时间(秒) +FORCE_KILL_WAIT=2 # 强制终止等待时间(秒) -# 颜色输出 +# 颜色定义 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' +CYAN='\033[0;36m' NC='\033[0m' # No Color +# 全局变量(用于交互式选择和批量操作) +SELECTED_JARS=() # 用户选择的JAR包数组 +MULTI_JAR_MODE=false # 是否为多JAR包模式 +PARSED_ACTION="" # 解析出的操作命令 +PARSED_JARS=() # 解析出的JAR包列表 + # ======================================== -# 函数定义 +# 工具函数 # ======================================== -# 打印带颜色的消息 -print_message() { - local color=$1 - local message=$2 - echo -e "${color}[$(date '+%Y-%m-%d %H:%M:%S')] ${message}${NC}" +# 打印错误消息 +print_error() { + echo -e "${RED}错误: $1${NC}" >&2 +} + +# 打印警告消息 +print_warning() { + echo -e "${YELLOW}⚠ $1${NC}" +} + +# 打印成功消息 +print_success() { + echo -e "${GREEN}✓ $1${NC}" +} + +# 打印信息消息 +print_info() { + echo -e "${BLUE}$1${NC}" +} + +# 验证Java环境的详细检查 +validate_java_environment() { + # 检查Java是否安装 + if ! command -v java >/dev/null 2>&1; then + print_error "Java未安装或不在PATH中" + print_info "请安装Java运行环境(JRE)或Java开发工具包(JDK)" + return 1 + fi + + # 检查Java版本 + local java_version + java_version=$(java -version 2>&1 | head -n 1 | cut -d'"' -f2) + + if [ -z "$java_version" ]; then + print_error "无法获取Java版本信息" + return 1 + fi + + print_info "检测到Java版本: $java_version" + + # 检查Java是否能正常运行 + if ! java -version >/dev/null 2>&1; then + print_error "Java环境异常,无法正常运行" + return 1 + fi + + return 0 +} + +# 检查磁盘空间 +check_disk_space() { + local required_space_mb="${1:-100}" # 默认需要100MB空间 + + # 获取当前目录的可用空间(MB) + local available_space + if command -v df >/dev/null 2>&1; then + available_space=$(df . | awk 'NR==2 {print int($4/1024)}') + + if [ "$available_space" -lt "$required_space_mb" ]; then + print_warning "磁盘空间不足,可用空间: ${available_space}MB,建议至少: ${required_space_mb}MB" + return 1 + fi + else + print_warning "无法检查磁盘空间(df命令不可用)" + fi + + return 0 +} + +# 检查文件权限 +check_file_permissions() { + local file_path="$1" + + # 检查文件是否存在 + if [ ! -f "$file_path" ]; then + print_error "文件不存在: $file_path" + return 1 + fi + + # 检查文件是否可读 + if [ ! -r "$file_path" ]; then + print_error "文件不可读: $file_path" + return 1 + fi + + return 0 +} + +# 验证JAR文件名的安全性 +validate_jar_name() { + local jar_name="$1" + + # 检查是否为空 + if [ -z "$jar_name" ]; then + print_error "JAR文件名不能为空" + return 1 + fi + + # 检查文件名长度(防止过长的文件名) + if [ ${#jar_name} -gt 255 ]; then + print_error "JAR文件名过长(超过255个字符)" + return 1 + fi + + # 检查是否包含危险字符(路径遍历攻击) + if [[ "$jar_name" == *".."* ]] || [[ "$jar_name" == *"/"* ]] || [[ "$jar_name" == *"\\"* ]]; then + print_error "JAR文件名包含非法字符(不允许路径分隔符或相对路径)" + return 1 + fi + + # 检查是否以.jar结尾 + if [[ ! "$jar_name" == *.jar ]]; then + print_error "文件必须是.jar格式" + return 1 + fi + + # 检查文件名是否包含特殊字符(可能导致命令注入) + if [[ "$jar_name" =~ [\;\|\&\$\`\<\>\(\)\{\}\[\]\"\'*\?\~] ]]; then + print_error "JAR文件名包含特殊字符,可能存在安全风险" + return 1 + fi + + return 0 +} + +# 验证用户输入的安全性 +validate_user_input() { + local input="$1" + local input_type="${2:-general}" + + # 检查输入长度 + if [ ${#input} -gt 1000 ]; then + print_error "输入内容过长" + return 1 + fi + + # 根据输入类型进行不同的验证 + case "$input_type" in + "number") + # 验证数字输入 + if [[ ! "$input" =~ ^[0-9,[:space:]]+$ ]] && [ "$input" != "all" ]; then + print_error "输入格式错误,只允许数字、逗号、空格或'all'" + return 1 + fi + ;; + "action") + # 验证操作命令 + if [[ ! "$input" =~ ^(start|stop|restart|status|help|-h|--help)$ ]]; then + print_error "无效的操作命令: $input" + return 1 + fi + ;; + "general") + # 通用输入验证,检查危险字符 + if [[ "$input" =~ [\;\|\&\$\`\<\>\(\)\{\}*\?\~] ]]; then + print_error "输入包含特殊字符,可能存在安全风险" + return 1 + fi + ;; + esac + + return 0 +} + +# 检查Java环境 +check_java_environment() { + if ! command -v java &> /dev/null; then + print_error "Java未安装或不在PATH中" + return 1 + fi + + # 使用更详细的Java环境验证 + if ! validate_java_environment; then + return 1 + fi + + # 检查磁盘空间 + if ! check_disk_space; then + print_warning "磁盘空间不足可能影响应用运行" + fi + + return 0 +} + +# 设置JAR包相关变量 +setup_jar_variables() { + local jar_name="$1" + + if [ -z "$jar_name" ]; then + jar_name="$JAR_NAME" + fi + + # 设置日志文件名(添加时间戳) + local jar_basename=$(basename "$jar_name" .jar) + local timestamp=$(date +"%Y%m%d_%H%M%S") + LOG_FILE="${jar_basename}_${timestamp}.log" +} + +# 等待进程停止 +wait_for_process_stop() { + local pid="$1" + local timeout="${2:-$STOP_TIMEOUT}" + local count=0 + + while [ $count -lt $timeout ]; do + if ! ps -p $pid > /dev/null 2>&1; then + return 0 + fi + sleep 1 + count=$((count + 1)) + done + return 1 +} + +# 根据昵称匹配JAR文件 +find_jar_by_nickname() { + local nickname="$1" + local found_jars=() + + # 如果昵称是 "all",返回所有JAR文件 + if [ "$nickname" = "all" ]; then + for jar in *.jar; do + if [ -f "$jar" ]; then + found_jars+=("$jar") + fi + done + else + # 模糊匹配JAR文件名 + for jar in *.jar; do + if [ -f "$jar" ] && [[ "$jar" == *"$nickname"* ]]; then + found_jars+=("$jar") + fi + done + fi + + # 输出匹配的JAR文件 + printf '%s\n' "${found_jars[@]}" +} + +# 解析JAR包参数列表 +parse_jar_arguments() { + local args=("$@") + local jar_list=() + local action="" + + # 如果没有参数,使用默认行为 + if [ ${#args[@]} -eq 0 ]; then + action="restart" + PARSED_ACTION="$action" + return + fi + + # 检查第一个参数是否是操作命令 + case "${args[0]}" in + "start"|"stop"|"restart"|"status"|"help"|"-h"|"--help") + action="${args[0]}" + + # 验证操作命令 + if ! validate_user_input "$action" "action"; then + return 1 + fi + + # 如果只有操作命令,使用原有逻辑 + if [ ${#args[@]} -eq 1 ]; then + PARSED_ACTION="$action" + return + fi + # 其余参数作为JAR包昵称 + for ((i=1; i<${#args[@]}; i++)); do + # 验证JAR包昵称输入 + if ! validate_user_input "${args[i]}" "general"; then + return 1 + fi + jar_list+=("${args[i]}") + done + ;; + *) + # 第一个参数不是操作命令,默认为restart操作 + action="restart" + # 所有参数都作为JAR包昵称 + for arg in "${args[@]}"; do + # 验证JAR包昵称输入 + if ! validate_user_input "$arg" "general"; then + return 1 + fi + jar_list+=("$arg") + done + ;; + esac + + # 设置全局变量 + PARSED_ACTION="$action" + PARSED_JARS=("${jar_list[@]}") } # 检查JAR文件是否存在 @@ -41,233 +333,643 @@ check_jar_exists() { # 如果JAR_NAME为空,自动检测当前目录下的JAR文件 if [ -z "$JAR_NAME" ]; then local jar_files=(*.jar) - local jar_count=${#jar_files[@]} # 检查是否存在JAR文件 - if [ ! -f "${jar_files[0]}" ]; then - print_message $RED "错误: 当前目录下没有找到JAR文件!" - print_message $YELLOW "请将JAR文件放在脚本同一目录下,或在脚本中指定JAR_NAME" - exit 1 + if [ ! -e "${jar_files[0]}" ]; then + print_error "当前目录下没有找到JAR文件!" + print_warning "请将JAR文件放在脚本同一目录下,或在脚本中指定JAR_NAME" + return 1 fi - # 如果只有一个JAR文件,自动使用它 - if [ $jar_count -eq 1 ]; then + # 如果只有一个JAR文件,自动使用 + if [ ${#jar_files[@]} -eq 1 ]; then JAR_NAME="${jar_files[0]}" - print_message $BLUE "自动检测到JAR文件: $JAR_NAME" + + # 验证JAR文件名的安全性 + if ! validate_jar_name "$JAR_NAME"; then + return 1 + fi + + print_info "自动检测到JAR文件: $JAR_NAME" else - print_message $RED "错误: 当前目录下有多个JAR文件,请在脚本中指定JAR_NAME:" + # 多个JAR文件时,让用户选择 + print_info "检测到多个JAR文件,请选择要启动的应用:" + echo "" + + # 显示选项列表 + local i=1 for jar in "${jar_files[@]}"; do - print_message $YELLOW " - $jar" + echo -e "${GREEN}[$i]${NC} $jar" + ((i++)) done - exit 1 - fi - else - # 检查指定的JAR文件是否存在 - if [ ! -f "$JAR_NAME" ]; then - print_message $RED "错误: JAR文件 '$JAR_NAME' 不存在!" - print_message $YELLOW "请检查JAR_NAME配置是否正确,或确保JAR文件在当前目录下" - exit 1 + echo "" + + # 提示用户输入 + echo -e "${YELLOW}输入选项:${NC}" + echo -e " - 单个序号: ${GREEN}1${NC} (选择第1个JAR包)" + echo -e " - 多个序号: ${GREEN}1,3,5${NC} 或 ${GREEN}1 3 5${NC} (选择多个JAR包)" + echo -e " - 全部选择: ${GREEN}all${NC} (选择所有JAR包)" + echo "" + echo -n -e "${YELLOW}请输入选择: ${NC}" + read -r choice + + # 验证用户输入 + if ! validate_user_input "$choice" "number"; then + return 1 + fi + + # 处理输入 + local selected_jars=() + + if [ "$choice" = "all" ]; then + # 选择所有JAR包 + selected_jars=("${jar_files[@]}") + print_success "已选择所有JAR包 (${#selected_jars[@]}个)" + else + # 解析序号输入(支持逗号分隔和空格分隔) + local numbers + if [[ "$choice" == *","* ]]; then + # 逗号分隔 + IFS=',' read -ra numbers <<< "$choice" + else + # 空格分隔 + read -ra numbers <<< "$choice" + fi + + # 验证并收集选中的JAR包 + local valid_selection=true + for num in "${numbers[@]}"; do + # 去除空格 + num=$(echo "$num" | tr -d ' ') + + if [[ "$num" =~ ^[0-9]+$ ]] && [ "$num" -ge 1 ] && [ "$num" -le ${#jar_files[@]} ]; then + selected_jars+=("${jar_files[$((num-1))]}") + else + print_error "无效的序号 '$num'!请输入1到${#jar_files[@]}之间的数字" + valid_selection=false + break + fi + done + + if [ "$valid_selection" = false ]; then + return 1 + fi + + # 去重 + selected_jars=($(printf '%s\n' "${selected_jars[@]}" | sort -u)) + + print_success "已选择 ${#selected_jars[@]} 个JAR包:" + for jar in "${selected_jars[@]}"; do + echo -e " - $jar" + done + fi + + # 验证所有选中的JAR文件名 + for jar in "${selected_jars[@]}"; do + if ! validate_jar_name "$jar"; then + return 1 + fi + done + + echo "" + + # 如果选择了多个JAR包,设置全局变量供批量操作使用 + if [ ${#selected_jars[@]} -gt 1 ]; then + SELECTED_JARS=("${selected_jars[@]}") + MULTI_JAR_MODE=true + # 设置第一个JAR作为默认JAR_NAME(兼容性) + JAR_NAME="${selected_jars[0]}" + else + # 单个JAR包 + JAR_NAME="${selected_jars[0]}" + MULTI_JAR_MODE=false + fi fi fi + + # 检查指定的JAR文件是否存在 + if [ ! -f "$JAR_NAME" ]; then + print_error "JAR文件 '$JAR_NAME' 不存在!" + print_warning "请检查JAR_NAME配置是否正确,或确保JAR文件在当前目录下" + return 1 + fi + + # 检查文件权限 + if ! check_file_permissions "$JAR_NAME"; then + return 1 + fi + + return 0 +} + +# 获取运行中的应用PID +get_app_pid() { + local jar_name="$1" + + if [ -z "$jar_name" ]; then + jar_name="$JAR_NAME" + fi + + # 通过进程名查找(更可靠的方法) + pgrep -f "$jar_name" | head -1 } -# 检查端口是否被占用 -check_port() { - local port=$1 - if command -v netstat >/dev/null 2>&1; then - netstat -tlnp 2>/dev/null | grep ":$port " >/dev/null - elif command -v ss >/dev/null 2>&1; then - ss -tlnp | grep ":$port " >/dev/null +# 检查应用是否正在运行 +is_running() { + local jar_name="$1" + local pid=$(get_app_pid "$jar_name") + + if [ -n "$pid" ]; then + return 0 # 正在运行 else - # 如果没有netstat和ss,尝试使用lsof - if command -v lsof >/dev/null 2>&1; then - lsof -i :$port >/dev/null 2>&1 - else - return 1 - fi + return 1 # 未运行 fi } -# 获取运行中的应用PID -get_app_pid() { - if [ -f "$PID_FILE" ]; then - local pid=$(cat "$PID_FILE") - # 检查进程是否真的在运行 - if ps -p $pid > /dev/null 2>&1; then - echo $pid - else - # PID文件存在但进程不存在,删除PID文件 - rm -f "$PID_FILE" - echo "" - fi +# 启动应用 +start_app() { + local jar_name="$1" + + if [ -z "$jar_name" ]; then + jar_name="$JAR_NAME" + fi + + if ! check_java_environment; then + return 1 + fi + + setup_jar_variables "$jar_name" + + if is_running "$jar_name"; then + print_warning "应用 $jar_name 已在运行中 (PID: $(get_app_pid "$jar_name"))" + return 0 + fi + + print_info "正在启动应用: $jar_name" + print_info "日志文件: $LOG_FILE" + + # 启动应用 + nohup java $JAVA_OPTS -jar "$jar_name" > "$LOG_FILE" 2>&1 & + + # 等待启动 + sleep "$STARTUP_WAIT" + + # 验证启动状态 + if is_running "$jar_name"; then + local actual_pid=$(get_app_pid "$jar_name") + print_success "应用 $jar_name 启动成功 (PID: $actual_pid)" + print_info "查看日志: tail -f $LOG_FILE" + return 0 else - # 通过进程名查找 - pgrep -f "$JAR_NAME" | head -1 + print_error "应用 $jar_name 启动失败" + print_warning "请检查日志文件: $LOG_FILE" + return 1 fi } # 停止应用 stop_app() { - local pid=$(get_app_pid) + local jar_name="$1" + + if [ -z "$jar_name" ]; then + jar_name="$JAR_NAME" + fi + + local pid=$(get_app_pid "$jar_name") if [ -z "$pid" ]; then - print_message $YELLOW "应用未运行" + print_warning "应用 $jar_name 未运行" return 0 fi - print_message $YELLOW "正在停止应用 (PID: $pid)..." + setup_jar_variables "$jar_name" - # 优雅停止 - kill $pid + print_info "正在停止应用 $jar_name (PID: $pid)..." + + # 发送TERM信号 + kill "$pid" 2>/dev/null # 等待进程停止 - local count=0 - while [ $count -lt 30 ]; do - if ! ps -p $pid > /dev/null 2>&1; then - print_message $GREEN "应用已成功停止" - rm -f "$PID_FILE" + if wait_for_process_stop "$pid"; then + print_success "应用 $jar_name 已成功停止" + return 0 + else + print_warning "应用 $jar_name 未在 ${STOP_TIMEOUT} 秒内停止,强制终止..." + kill -9 "$pid" 2>/dev/null + sleep "$FORCE_KILL_WAIT" + + # 再次检查 + if [ -z "$(get_app_pid "$jar_name")" ]; then + print_success "应用 $jar_name 已强制停止" return 0 + else + print_error "无法停止应用 $jar_name" + return 1 fi - sleep 1 - count=$((count + 1)) - done + fi +} + +# 重启应用 +restart_app() { + local jar_name="$1" - # 如果优雅停止失败,强制停止 - print_message $YELLOW "优雅停止超时,强制停止应用..." - kill -9 $pid 2>/dev/null + if [ -z "$jar_name" ]; then + jar_name="$JAR_NAME" + fi - sleep 2 - if ! ps -p $pid > /dev/null 2>&1; then - print_message $GREEN "应用已强制停止" - rm -f "$PID_FILE" - return 0 - else - print_message $RED "停止应用失败" + if ! check_java_environment; then return 1 fi + + setup_jar_variables "$jar_name" + + print_info "正在重启应用: $jar_name" + + if is_running "$jar_name"; then + print_info "正在停止应用..." + stop_app "$jar_name" + else + print_info "应用未运行,直接启动" + fi + + print_info "正在启动应用..." + start_app "$jar_name" } -# 启动应用 -start_app() { - print_message $BLUE "正在启动应用..." +# 显示应用状态 +show_status() { + local jar_name="$1" - # 检查端口是否被占用 - if check_port $PORT; then - print_message $RED "端口 $PORT 已被占用,请检查是否有其他应用在使用该端口" + if [ -z "$jar_name" ]; then + jar_name="$JAR_NAME" + fi + + setup_jar_variables "$jar_name" + + print_info "=== 应用状态 ===" + echo "JAR包: $jar_name" + echo "JVM参数: $JAVA_OPTS" + echo "日志文件: $LOG_FILE" + echo "" + + local pid=$(get_app_pid "$jar_name") + + if [ -n "$pid" ]; then + print_success "✓ 应用正在运行 (PID: $pid)" + + # 显示进程信息 + if command -v ps >/dev/null 2>&1; then + echo "" + echo "进程详情:" + ps -p "$pid" -o pid,ppid,cmd --no-headers 2>/dev/null || echo " 无法获取进程详情" + fi + else + print_warning "✗ 应用未运行" + fi +} + +# 批量启动应用 +batch_start_app() { + local jar_files=("$@") + local success_count=0 + local total_count=${#jar_files[@]} + + if [ $total_count -eq 0 ]; then + print_error "没有指定要启动的JAR包" return 1 fi - # 启动应用 - nohup java $JAVA_OPTS -jar "$JAR_NAME" --server.port=$PORT > "$LOG_FILE" 2>&1 & - local pid=$! - - # 保存PID - echo $pid > "$PID_FILE" - - # 等待应用启动 - print_message $BLUE "等待应用启动..." - sleep 3 - - # 检查进程是否还在运行 - if ps -p $pid > /dev/null 2>&1; then - # 检查端口是否监听 - local count=0 - while [ $count -lt 30 ]; do - if check_port $PORT; then - print_message $GREEN "应用启动成功!" - print_message $GREEN "PID: $pid" - print_message $GREEN "端口: $PORT" - print_message $GREEN "日志文件: $LOG_FILE" - return 0 - fi - sleep 1 - count=$((count + 1)) - done + if ! check_java_environment; then + return 1 + fi + + print_info "=== 批量启动应用 ===" + print_info "准备启动 $total_count 个JAR包..." + echo "" + + for jar_file in "${jar_files[@]}"; do + print_info "正在启动: $jar_file" + + # 检查JAR文件是否存在 + if [ ! -f "$jar_file" ]; then + print_error " JAR文件不存在: $jar_file" + continue + fi + + setup_jar_variables "$jar_file" + + # 检查是否已经在运行 + if is_running "$jar_file"; then + print_warning " $jar_file 已经在运行" + ((success_count++)) + continue + fi + + # 启动应用 + echo " JAR包: $jar_file" + echo " JVM参数: $JAVA_OPTS" + echo " 日志文件: $LOG_FILE" + + nohup java $JAVA_OPTS -jar "$jar_file" > "$LOG_FILE" 2>&1 & - print_message $YELLOW "应用进程已启动,但端口 $PORT 未监听,请检查日志文件: $LOG_FILE" + sleep "$STARTUP_WAIT" + + if is_running "$jar_file"; then + local pid=$(get_app_pid "$jar_file") + print_success " $jar_file 启动成功 (PID: $pid)" + ((success_count++)) + else + print_error " $jar_file 启动失败" + print_warning " 请检查日志文件: $LOG_FILE" + fi + echo "" + done + + print_info "=== 批量启动完成 ===" + if [ $success_count -eq $total_count ]; then + print_success "成功启动: $success_count/$total_count" + else + print_warning "成功启动: $success_count/$total_count" + fi +} + +# 批量停止应用 +batch_stop_app() { + local jar_files=("$@") + local success_count=0 + local total_count=${#jar_files[@]} + + if [ $total_count -eq 0 ]; then + print_error "没有指定要停止的JAR包" return 1 + fi + + print_info "=== 批量停止应用 ===" + print_info "准备停止 $total_count 个JAR包..." + echo "" + + for jar_file in "${jar_files[@]}"; do + print_info "正在停止: $jar_file" + + local pid=$(get_app_pid "$jar_file") + + if [ -z "$pid" ]; then + print_warning " $jar_file 未运行" + ((success_count++)) + continue + fi + + setup_jar_variables "$jar_file" + + echo " 正在停止进程 (PID: $pid)..." + kill "$pid" 2>/dev/null + + # 等待进程停止 + if wait_for_process_stop "$pid"; then + print_success " $jar_file 停止成功" + ((success_count++)) + else + print_warning " 强制停止进程..." + kill -9 "$pid" 2>/dev/null + sleep "$FORCE_KILL_WAIT" + + if [ -z "$(get_app_pid "$jar_file")" ]; then + print_success " $jar_file 强制停止成功" + ((success_count++)) + else + print_error " $jar_file 停止失败" + fi + fi + echo "" + done + + print_info "=== 批量停止完成 ===" + if [ $success_count -eq $total_count ]; then + print_success "成功停止: $success_count/$total_count" else - print_message $RED "应用启动失败,请检查日志文件: $LOG_FILE" - rm -f "$PID_FILE" + print_warning "成功停止: $success_count/$total_count" + fi +} + +# 批量重启应用 +batch_restart_app() { + local jar_files=("$@") + + if [ ${#jar_files[@]} -eq 0 ]; then + print_error "没有指定要重启的JAR包" + return 1 + fi + + if ! check_java_environment; then return 1 fi + + print_info "=== 批量重启应用 ===" + print_info "准备重启 ${#jar_files[@]} 个JAR包..." + echo "" + + # 先停止所有应用 + batch_stop_app "${jar_files[@]}" + + echo "" + sleep "$STARTUP_WAIT" + + # 再启动所有应用 + batch_start_app "${jar_files[@]}" } -# 显示应用状态 -show_status() { - local pid=$(get_app_pid) +# 批量显示状态 +batch_show_status() { + local jar_files=("$@") - if [ -n "$pid" ]; then - print_message $GREEN "应用正在运行" - print_message $GREEN "PID: $pid" - print_message $GREEN "端口: $PORT" + if [ ${#jar_files[@]} -eq 0 ]; then + print_error "没有指定要查看状态的JAR包" + return 1 + fi + + print_info "=== 批量应用状态 ===" + echo "" + + local running_count=0 + local total_count=${#jar_files[@]} + + for jar_file in "${jar_files[@]}"; do + echo -e "${CYAN}--- $jar_file ---${NC}" + + setup_jar_variables "$jar_file" + + echo "JAR包: $jar_file" + echo "JVM参数: $JAVA_OPTS" + echo "日志文件: $LOG_FILE" + + local pid=$(get_app_pid "$jar_file") - if check_port $PORT; then - print_message $GREEN "端口状态: 正在监听" + if [ -n "$pid" ]; then + print_success "✓ 正在运行 (PID: $pid)" + ((running_count++)) else - print_message $YELLOW "端口状态: 未监听" + print_warning "✗ 未运行" fi + echo "" + done + + print_info "=== 状态统计 ===" + if [ $running_count -eq $total_count ]; then + print_success "运行中: $running_count/$total_count (全部运行)" + elif [ $running_count -eq 0 ]; then + print_warning "运行中: $running_count/$total_count (全部停止)" else - print_message $YELLOW "应用未运行" + print_warning "运行中: $running_count/$total_count (部分运行)" fi } # 显示帮助信息 show_help() { - echo "Java应用管理脚本" - echo "" - echo "使用方法:" - echo " $0 [选项]" - echo "" - echo "选项:" - echo " start 启动应用" - echo " stop 停止应用" - echo " restart 重启应用" - echo " status 显示应用状态" - echo " help 显示帮助信息" - echo "" - echo "配置:" - echo " JAR包名称: ${JAR_NAME:-"自动检测"}" - echo " 端口号: $PORT" - echo "" - echo "注意: 如果目录下只有一个JAR文件,可以将JAR_NAME留空实现自动检测" + cat << 'EOF' +Java应用管理脚本 + +用法: + ./restart-java-app.sh {start|stop|restart|status|help} [jar昵称1] [jar昵称2] ... + ./restart-java-app.sh [jar昵称1] [jar昵称2] ... # 默认执行restart操作 + +命令说明: + start - 启动应用 + stop - 停止应用 + restart - 重启应用(默认操作) + status - 查看应用状态 + help - 显示帮助信息 + +JAR包昵称说明: + • 支持模糊匹配:输入JAR包名称的一部分即可匹配 + • 支持多个昵称:可以同时指定多个JAR包昵称进行批量操作 + • 支持 'all' 参数:使用 'all' 可以对所有JAR包进行操作 + • 自动检测:如果不指定昵称,脚本会自动检测当前目录下的JAR包 + +使用示例: + # 单JAR包操作 + ./restart-java-app.sh start # 启动(自动检测JAR包) + ./restart-java-app.sh restart myapp # 重启包含"myapp"的JAR包 + ./restart-java-app.sh status user-service # 查看user-service状态 + + # 多JAR包批量操作 + ./restart-java-app.sh start user order # 启动user和order相关的JAR包 + ./restart-java-app.sh stop all # 停止所有JAR包 + ./restart-java-app.sh restart user order pay # 重启多个服务 + + # 默认重启操作(省略restart命令) + ./restart-java-app.sh myapp # 重启myapp + ./restart-java-app.sh user order # 重启user和order服务 + +配置说明: + • JAR_NAME: JAR文件名(留空自动检测) + • JAVA_OPTS: JVM参数(默认: -Xms512m -Xmx1024m) + • 日志文件: 自动生成为 {jar名称}.log + +注意事项: + • 脚本需要在JAR文件所在目录运行 + • 确保有足够的权限启动/停止进程 + • 日志文件会在同一目录下生成 + • 支持优雅停止,超时后会强制终止进程 +EOF +} + +# 执行命令的统一处理函数 +execute_command() { + local action="$1" + shift + local jar_files=("$@") + + case "$action" in + "start") + if [ ${#jar_files[@]} -gt 1 ]; then + batch_start_app "${jar_files[@]}" + else + start_app "${jar_files[0]}" + fi + ;; + "stop") + if [ ${#jar_files[@]} -gt 1 ]; then + batch_stop_app "${jar_files[@]}" + else + stop_app "${jar_files[0]}" + fi + ;; + "restart") + if [ ${#jar_files[@]} -gt 1 ]; then + batch_restart_app "${jar_files[@]}" + else + restart_app "${jar_files[0]}" + fi + ;; + "status") + if [ ${#jar_files[@]} -gt 1 ]; then + batch_show_status "${jar_files[@]}" + else + show_status "${jar_files[0]}" + fi + ;; + "help"|"-h"|"--help") + show_help + ;; + *) + print_error "未知命令 '$action'" + show_help + exit 1 + ;; + esac } # ======================================== # 主程序 # ======================================== -# 检查JAR文件是否存在 -check_jar_exists - -# 处理命令行参数 -case "${1:-restart}" in - "start") - start_app - ;; - "stop") - stop_app - ;; - "restart") - print_message $BLUE "重启应用..." - stop_app - if [ $? -eq 0 ]; then - sleep 2 - start_app - else - print_message $RED "停止应用失败,取消重启" +# 解析命令行参数 +parse_jar_arguments "$@" + +# 如果解析出了JAR包参数,执行批量操作 +if [ ${#PARSED_JARS[@]} -gt 0 ]; then + # 收集所有匹配的JAR文件 + all_jar_files=() + + for nickname in "${PARSED_JARS[@]}"; do + matched_jars=($(find_jar_by_nickname "$nickname")) + + if [ ${#matched_jars[@]} -eq 0 ]; then + print_error "未找到匹配昵称 '$nickname' 的JAR文件" exit 1 fi - ;; - "status") - show_status - ;; - "help"|"-h"|"--help") - show_help - ;; - *) - print_message $RED "未知选项: $1" - show_help + + # 添加到总列表 + all_jar_files+=("${matched_jars[@]}") + done + + # 去重 + unique_jars=($(printf '%s\n' "${all_jar_files[@]}" | sort -u)) + + print_info "找到 ${#unique_jars[@]} 个JAR文件:" + for jar in "${unique_jars[@]}"; do + echo " - $jar" + done + echo "" + + execute_command "$PARSED_ACTION" "${unique_jars[@]}" +else + # 检查JAR文件是否存在(可能触发交互式选择) + if ! check_jar_exists; then exit 1 - ;; -esac \ No newline at end of file + fi + + # 处理命令行参数 + ACTION="${PARSED_ACTION:-restart}" + + # 检查是否在check_jar_exists中选择了多个JAR包 + if [ "$MULTI_JAR_MODE" = true ] && [ ${#SELECTED_JARS[@]} -gt 0 ]; then + print_info "检测到多JAR包模式,将对 ${#SELECTED_JARS[@]} 个JAR包执行 '$ACTION' 操作" + echo "" + + execute_command "$ACTION" "${SELECTED_JARS[@]}" + else + # 单JAR包操作 + execute_command "$ACTION" "$JAR_NAME" + fi +fi \ No newline at end of file From 1384374d94a6dd6c92c16ff15957413978c9ba6c Mon Sep 17 00:00:00 2001 From: huliyong <2783385703@qq.com> Date: Sat, 27 Sep 2025 11:33:25 +0800 Subject: [PATCH 4/6] =?UTF-8?q?feat(=E8=84=9A=E6=9C=AC):=20=E6=94=B9?= =?UTF-8?q?=E8=BF=9B=E6=97=A5=E5=BF=97=E6=96=87=E4=BB=B6=E5=AD=98=E5=82=A8?= =?UTF-8?q?=E7=BB=93=E6=9E=84=EF=BC=8C=E8=87=AA=E5=8A=A8=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修改重启脚本以将日志文件存储在按JAR包名称组织的子目录中,并自动创建所需目录结构。同时更新相关文档以反映此变更。 --- README-restart-script.md | 14 ++++++++------ restart-java-app.sh | 17 ++++++++++++++++- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/README-restart-script.md b/README-restart-script.md index 8db6a8d..ffc262e 100644 --- a/README-restart-script.md +++ b/README-restart-script.md @@ -117,17 +117,19 @@ chmod +x restart-java-app.sh 脚本运行时会创建以下文件: -- **日志文件**: 自动生成,格式为 `{JAR包名}_{启动时间}.log`,例如:`my-app_20240115_143025.log` +- **日志文件**: 自动生成,存储在 `log/{JAR包名}/` 目录下,格式为 `{JAR包名}_{启动时间}.log` - **PID文件**: `app.pid` - 应用进程PID文件 ### 日志文件命名规则 -- **自动生成模式**(推荐):当 `LOG_FILE=""` 时,系统会自动生成日志文件名 -- **格式**: `{JAR包名称}_{年月日_时分秒}.log` +- **自动生成模式**(推荐):当 `LOG_FILE=""` 时,系统会自动生成日志文件名和路径 +- **目录结构**: `log/{JAR包名称}/` +- **文件格式**: `{JAR包名称}_{年月日_时分秒}.log` - **示例**: - JAR包:`my-spring-boot-app.jar` - 启动时间:2024年1月15日 14:30:25 - - 日志文件:`my-spring-boot-app_20240115_143025.log` + - 日志文件路径:`log/my-spring-boot-app/my-spring-boot-app_20240115_143025.log` +- **目录自动创建**:脚本会自动创建 `log/{JAR包名}/` 目录结构 - **手动指定**:如果在脚本中设置了 `LOG_FILE="custom.log"`,则使用指定的文件名 ## 常见问题 @@ -258,7 +260,7 @@ JAR_NAME="my-app.jar" 正在启动应用... JAR包: my-api-service.jar JVM参数: -Xms512m -Xmx1024m -日志文件: my-api-service_20240115_143025.log +日志文件: log/my-api-service/my-api-service_20240115_143025.log ✓ 应用启动成功 (PID: 12345) -日志文件: my-api-service_20240115_143025.log +日志文件: log/my-api-service/my-api-service_20240115_143025.log ``` \ No newline at end of file diff --git a/restart-java-app.sh b/restart-java-app.sh index 011aabb..ce74c9c 100644 --- a/restart-java-app.sh +++ b/restart-java-app.sh @@ -228,7 +228,22 @@ setup_jar_variables() { # 设置日志文件名(添加时间戳) local jar_basename=$(basename "$jar_name" .jar) local timestamp=$(date +"%Y%m%d_%H%M%S") - LOG_FILE="${jar_basename}_${timestamp}.log" + + # 创建日志目录结构:log/jar包名称/ + local log_dir="log/${jar_basename}" + + # 确保日志目录存在 + if [ ! -d "$log_dir" ]; then + mkdir -p "$log_dir" 2>/dev/null + if [ $? -ne 0 ]; then + print_warning "无法创建日志目录 $log_dir,使用当前目录" + LOG_FILE="${jar_basename}_${timestamp}.log" + else + LOG_FILE="${log_dir}/${jar_basename}_${timestamp}.log" + fi + else + LOG_FILE="${log_dir}/${jar_basename}_${timestamp}.log" + fi } # 等待进程停止 From 6c6997f79092225d61eea1bcde3a36ee8b7db602 Mon Sep 17 00:00:00 2001 From: huliyong <2783385703@qq.com> Date: Sat, 27 Sep 2025 11:38:49 +0800 Subject: [PATCH 5/6] =?UTF-8?q?refactor(script):=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E9=BB=98=E8=AE=A4JVM=E5=8F=82=E6=95=B0=E4=BB=A5=E8=8A=82?= =?UTF-8?q?=E7=9C=81=E5=86=85=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更新重启脚本和文档,将默认JVM参数设为空值以使用JVM默认设置 --- README-restart-script.md | 4 ++-- restart-java-app.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README-restart-script.md b/README-restart-script.md index ffc262e..e52325a 100644 --- a/README-restart-script.md +++ b/README-restart-script.md @@ -42,7 +42,7 @@ JAR_NAME="" # JAR包名称(留空则自动检测) | 参数 | 说明 | 默认值 | |------|------|--------| -| `JAVA_OPTS` | JVM启动参数 | `"-Xms512m -Xmx1024m"` | +| `JAVA_OPTS` | JVM启动参数 | `""` (空值,使用JVM默认设置,节省内存) | | `LOG_FILE` | 日志文件名(留空则自动生成) | `""` | | `PID_FILE` | PID文件名 | `"app.pid"` | @@ -259,7 +259,7 @@ JAR_NAME="my-app.jar" 正在启动应用... JAR包: my-api-service.jar -JVM参数: -Xms512m -Xmx1024m +JVM参数: (使用默认设置) 日志文件: log/my-api-service/my-api-service_20240115_143025.log ✓ 应用启动成功 (PID: 12345) 日志文件: log/my-api-service/my-api-service_20240115_143025.log diff --git a/restart-java-app.sh b/restart-java-app.sh index ce74c9c..af25083 100644 --- a/restart-java-app.sh +++ b/restart-java-app.sh @@ -7,7 +7,7 @@ # 全局配置 JAR_NAME="" # JAR文件名(为空时自动检测) -JAVA_OPTS="-Xms512m -Xmx1024m" # JVM参数 +JAVA_OPTS="" # JVM参数(为空时使用默认值,节省内存) LOG_FILE="" # 日志文件名(自动生成) # 运行时配置常量 From 234041fe535d094f5c09ed05b4d6403dc84a6bc1 Mon Sep 17 00:00:00 2001 From: huliyong <2783385703@qq.com> Date: Sat, 27 Sep 2025 22:53:39 +0800 Subject: [PATCH 6/6] 1 --- README-restart-script.md | 266 ------------- restart-java-app.sh | 990 ----------------------------------------------- 2 files changed, 1256 deletions(-) delete mode 100644 README-restart-script.md delete mode 100644 restart-java-app.sh diff --git a/README-restart-script.md b/README-restart-script.md deleted file mode 100644 index e52325a..0000000 --- a/README-restart-script.md +++ /dev/null @@ -1,266 +0,0 @@ -# Java应用启动脚本使用说明 - -## 概述 - -`restart-java-app.sh` 是一个功能强大的Java应用管理脚本,支持启动、停止、重启和状态查看等操作。脚本支持单个JAR包操作和多JAR包批量操作,提供灵活的JAR包昵称匹配功能。 - -## 主要功能 - -- ✅ **自动JAR包检测** - 当目录中只有一个JAR包时自动选择 -- ✅ **多JAR包选择** - 当目录中有多个JAR包时提供交互式选择菜单 -- ✅ **多JAR包批量操作** - 支持通过昵称同时操作多个JAR包 -- ✅ **模糊匹配** - 支持通过昵称模糊匹配JAR文件名 -- ✅ **全量操作** - 支持使用'all'参数操作所有JAR包 -- ✅ **进程管理** - 自动管理应用进程的启动和停止 -- ✅ **日志管理** - 自动生成基于JAR包名和时间戳的日志文件 -- ✅ **状态监控** - 实时查看应用运行状态 -- ✅ **优雅停止** - 先尝试正常停止,必要时强制终止 - -## 配置参数 - -在使用脚本前,需要修改脚本开头的配置参数: - -```bash -# ======================================== -# 配置参数 - 请根据实际情况修改 -# ======================================== -JAR_NAME="" # JAR包名称(留空则自动检测) -``` - -### 主要配置项 - -| 参数 | 说明 | 示例 | -|------|------|------| -| `JAR_NAME` | JAR包文件名(留空可自动检测) | `"my-spring-boot-app.jar"` 或 `""` | - -**自动检测功能**: 如果将 `JAR_NAME` 留空(`JAR_NAME=""`),脚本会自动检测当前目录下的JAR文件: -- 如果只有一个JAR文件,自动使用该文件 -- 如果有多个JAR文件,会显示选择菜单让用户选择具体的JAR文件 -- 如果没有JAR文件,会提示错误 - -### 可选配置项 - -| 参数 | 说明 | 默认值 | -|------|------|--------| -| `JAVA_OPTS` | JVM启动参数 | `""` (空值,使用JVM默认设置,节省内存) | -| `LOG_FILE` | 日志文件名(留空则自动生成) | `""` | -| `PID_FILE` | PID文件名 | `"app.pid"` | - -## 使用方法 - -### 1. 设置执行权限 - -```bash -chmod +x restart-java-app.sh -``` - -### 2. 基本用法 - -```bash -# 重启应用(默认操作) -./restart-java-app.sh - -# 或者明确指定重启 -./restart-java-app.sh restart -``` - -### 3. 其他命令 - -```bash -# 启动应用 -./restart-java-app.sh start - -# 停止应用 -./restart-java-app.sh stop - -# 查看应用状态 -./restart-java-app.sh status - -# 显示帮助信息 -./restart-java-app.sh help -``` - -## 命令详解 - -### start - 启动应用 -- 检查JAR文件是否存在 -- 启动Java应用 -- 等待应用完全启动 -- 验证应用启动状态 - -### stop - 停止应用 -- 查找应用进程PID -- 发送TERM信号优雅停止 -- 等待最多30秒 -- 超时后强制停止(KILL信号) -- 清理PID文件 - -### restart - 重启应用 -- 先执行stop操作 -- 等待2秒 -- 再执行start操作 - -### status - 查看状态 -- 显示应用运行状态 -- 显示进程PID - -## 输出说明 - -脚本使用彩色输出来区分不同类型的消息: - -- 🔴 **红色**: 错误信息 -- 🟢 **绿色**: 成功信息 -- 🟡 **黄色**: 警告信息 -- 🔵 **蓝色**: 操作信息 - -## 文件说明 - -脚本运行时会创建以下文件: - -- **日志文件**: 自动生成,存储在 `log/{JAR包名}/` 目录下,格式为 `{JAR包名}_{启动时间}.log` -- **PID文件**: `app.pid` - 应用进程PID文件 - -### 日志文件命名规则 - -- **自动生成模式**(推荐):当 `LOG_FILE=""` 时,系统会自动生成日志文件名和路径 -- **目录结构**: `log/{JAR包名称}/` -- **文件格式**: `{JAR包名称}_{年月日_时分秒}.log` -- **示例**: - - JAR包:`my-spring-boot-app.jar` - - 启动时间:2024年1月15日 14:30:25 - - 日志文件路径:`log/my-spring-boot-app/my-spring-boot-app_20240115_143025.log` -- **目录自动创建**:脚本会自动创建 `log/{JAR包名}/` 目录结构 -- **手动指定**:如果在脚本中设置了 `LOG_FILE="custom.log"`,则使用指定的文件名 - -## 常见问题 - -### Q: 提示"JAR文件不存在" -**A**: -- 如果使用自动检测模式(`JAR_NAME=""`),确保当前目录下有且仅有一个JAR文件 -- 如果指定了JAR文件名,检查`JAR_NAME`配置是否正确,确保JAR文件在脚本同一目录下 - -### Q: 提示"当前目录下有多个JAR文件" -**A**: 当目录下有多个JAR文件时,脚本会显示选择菜单,按提示输入对应的序号即可选择要启动的JAR文件。你也可以在脚本中明确指定`JAR_NAME`,例如:`JAR_NAME="my-app.jar"` - -### Q: 应用启动失败 -**A**: 查看`app.log`日志文件,检查具体错误原因。 - -### Q: 无法停止应用 -**A**: 脚本会先尝试优雅停止,30秒后自动强制停止。 - -## 系统要求 - -- Linux操作系统 -- Bash Shell -- Java运行环境 - -## 注意事项 - -1. 确保脚本有执行权限 -2. 确保Java环境已正确安装 -3. 确保JAR文件路径正确 -4. 建议在生产环境使用前先在测试环境验证 -5. 定期检查日志文件大小,避免占用过多磁盘空间 - -## 示例 - -### 示例1: 自动检测模式(推荐) - -如果你的目录下只有一个JAR文件,可以使用自动检测模式: - -1. 保持脚本配置为默认: -```bash -JAR_NAME="" # 留空,自动检测 -``` - -2. 启动应用: -```bash -# 启动应用 -./restart-java-app.sh start - -# 重启应用 -./restart-java-app.sh restart -``` - -脚本会自动检测并使用目录下唯一的JAR文件。 - -### 示例2: 指定JAR文件名 - -假设你有一个名为`my-app.jar`的Spring Boot应用: - -1. 修改脚本配置: -```bash -JAR_NAME="my-app.jar" -``` - -2. 使用不同方式启动: -```bash -# 启动应用 -./restart-java-app.sh start - -# 重启应用 -./restart-java-app.sh restart - -# 停止应用 -./restart-java-app.sh stop -``` - -3. 查看状态: -```bash -./restart-java-app.sh status -``` - -### 示例3: 多JAR文件选择 - -当目录下有多个JAR文件时,脚本会提供交互式选择: - -```bash -# 启动脚本,会显示选择菜单 -./restart-java-app.sh start -``` - -## 交互式JAR包选择 - -当脚本检测到多个JAR文件时,会提供交互式选择界面: - -```bash -检测到多个JAR文件,请选择要启动的应用: - -[1] user-service-1.0.0.jar -[2] order-service-1.0.0.jar -[3] payment-service-1.0.0.jar - -输入选项: - - 单个序号: 1 (选择第1个JAR包) - - 多个序号: 1,3,5 或 1 3 5 (选择多个JAR包) - - 全部选择: all (选择所有JAR包) - -请输入选择: -``` - -**支持的输入格式:** -- `1` - 选择单个JAR包 -- `1,3,5` - 选择多个JAR包(逗号分隔) -- `1 3 5` - 选择多个JAR包(空格分隔) -- `all` - 选择所有JAR包 - -选择多个JAR包时,脚本会自动切换到批量操作模式。 - -输出示例: -``` -检测到多个JAR文件,请选择要启动的应用: - -[1] my-web-app.jar -[2] my-api-service.jar -[3] my-batch-job.jar - -请输入序号 (1-3): 2 -✓ 已选择: my-api-service.jar - -正在启动应用... -JAR包: my-api-service.jar -JVM参数: (使用默认设置) -日志文件: log/my-api-service/my-api-service_20240115_143025.log -✓ 应用启动成功 (PID: 12345) -日志文件: log/my-api-service/my-api-service_20240115_143025.log -``` \ No newline at end of file diff --git a/restart-java-app.sh b/restart-java-app.sh deleted file mode 100644 index af25083..0000000 --- a/restart-java-app.sh +++ /dev/null @@ -1,990 +0,0 @@ -#!/bin/bash - -# ======================================== -# Java应用启动/重启脚本 -# 使用方法: ./restart-java-app.sh -# ======================================== - -# 全局配置 -JAR_NAME="" # JAR文件名(为空时自动检测) -JAVA_OPTS="" # JVM参数(为空时使用默认值,节省内存) -LOG_FILE="" # 日志文件名(自动生成) - -# 运行时配置常量 -STOP_TIMEOUT=15 # 停止超时时间(秒) -STARTUP_WAIT=3 # 启动等待时间(秒) -FORCE_KILL_WAIT=2 # 强制终止等待时间(秒) - -# 颜色定义 -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -CYAN='\033[0;36m' -NC='\033[0m' # No Color - -# 全局变量(用于交互式选择和批量操作) -SELECTED_JARS=() # 用户选择的JAR包数组 -MULTI_JAR_MODE=false # 是否为多JAR包模式 -PARSED_ACTION="" # 解析出的操作命令 -PARSED_JARS=() # 解析出的JAR包列表 - -# ======================================== -# 工具函数 -# ======================================== - -# 打印错误消息 -print_error() { - echo -e "${RED}错误: $1${NC}" >&2 -} - -# 打印警告消息 -print_warning() { - echo -e "${YELLOW}⚠ $1${NC}" -} - -# 打印成功消息 -print_success() { - echo -e "${GREEN}✓ $1${NC}" -} - -# 打印信息消息 -print_info() { - echo -e "${BLUE}$1${NC}" -} - -# 验证Java环境的详细检查 -validate_java_environment() { - # 检查Java是否安装 - if ! command -v java >/dev/null 2>&1; then - print_error "Java未安装或不在PATH中" - print_info "请安装Java运行环境(JRE)或Java开发工具包(JDK)" - return 1 - fi - - # 检查Java版本 - local java_version - java_version=$(java -version 2>&1 | head -n 1 | cut -d'"' -f2) - - if [ -z "$java_version" ]; then - print_error "无法获取Java版本信息" - return 1 - fi - - print_info "检测到Java版本: $java_version" - - # 检查Java是否能正常运行 - if ! java -version >/dev/null 2>&1; then - print_error "Java环境异常,无法正常运行" - return 1 - fi - - return 0 -} - -# 检查磁盘空间 -check_disk_space() { - local required_space_mb="${1:-100}" # 默认需要100MB空间 - - # 获取当前目录的可用空间(MB) - local available_space - if command -v df >/dev/null 2>&1; then - available_space=$(df . | awk 'NR==2 {print int($4/1024)}') - - if [ "$available_space" -lt "$required_space_mb" ]; then - print_warning "磁盘空间不足,可用空间: ${available_space}MB,建议至少: ${required_space_mb}MB" - return 1 - fi - else - print_warning "无法检查磁盘空间(df命令不可用)" - fi - - return 0 -} - -# 检查文件权限 -check_file_permissions() { - local file_path="$1" - - # 检查文件是否存在 - if [ ! -f "$file_path" ]; then - print_error "文件不存在: $file_path" - return 1 - fi - - # 检查文件是否可读 - if [ ! -r "$file_path" ]; then - print_error "文件不可读: $file_path" - return 1 - fi - - return 0 -} - -# 验证JAR文件名的安全性 -validate_jar_name() { - local jar_name="$1" - - # 检查是否为空 - if [ -z "$jar_name" ]; then - print_error "JAR文件名不能为空" - return 1 - fi - - # 检查文件名长度(防止过长的文件名) - if [ ${#jar_name} -gt 255 ]; then - print_error "JAR文件名过长(超过255个字符)" - return 1 - fi - - # 检查是否包含危险字符(路径遍历攻击) - if [[ "$jar_name" == *".."* ]] || [[ "$jar_name" == *"/"* ]] || [[ "$jar_name" == *"\\"* ]]; then - print_error "JAR文件名包含非法字符(不允许路径分隔符或相对路径)" - return 1 - fi - - # 检查是否以.jar结尾 - if [[ ! "$jar_name" == *.jar ]]; then - print_error "文件必须是.jar格式" - return 1 - fi - - # 检查文件名是否包含特殊字符(可能导致命令注入) - if [[ "$jar_name" =~ [\;\|\&\$\`\<\>\(\)\{\}\[\]\"\'*\?\~] ]]; then - print_error "JAR文件名包含特殊字符,可能存在安全风险" - return 1 - fi - - return 0 -} - -# 验证用户输入的安全性 -validate_user_input() { - local input="$1" - local input_type="${2:-general}" - - # 检查输入长度 - if [ ${#input} -gt 1000 ]; then - print_error "输入内容过长" - return 1 - fi - - # 根据输入类型进行不同的验证 - case "$input_type" in - "number") - # 验证数字输入 - if [[ ! "$input" =~ ^[0-9,[:space:]]+$ ]] && [ "$input" != "all" ]; then - print_error "输入格式错误,只允许数字、逗号、空格或'all'" - return 1 - fi - ;; - "action") - # 验证操作命令 - if [[ ! "$input" =~ ^(start|stop|restart|status|help|-h|--help)$ ]]; then - print_error "无效的操作命令: $input" - return 1 - fi - ;; - "general") - # 通用输入验证,检查危险字符 - if [[ "$input" =~ [\;\|\&\$\`\<\>\(\)\{\}*\?\~] ]]; then - print_error "输入包含特殊字符,可能存在安全风险" - return 1 - fi - ;; - esac - - return 0 -} - -# 检查Java环境 -check_java_environment() { - if ! command -v java &> /dev/null; then - print_error "Java未安装或不在PATH中" - return 1 - fi - - # 使用更详细的Java环境验证 - if ! validate_java_environment; then - return 1 - fi - - # 检查磁盘空间 - if ! check_disk_space; then - print_warning "磁盘空间不足可能影响应用运行" - fi - - return 0 -} - -# 设置JAR包相关变量 -setup_jar_variables() { - local jar_name="$1" - - if [ -z "$jar_name" ]; then - jar_name="$JAR_NAME" - fi - - # 设置日志文件名(添加时间戳) - local jar_basename=$(basename "$jar_name" .jar) - local timestamp=$(date +"%Y%m%d_%H%M%S") - - # 创建日志目录结构:log/jar包名称/ - local log_dir="log/${jar_basename}" - - # 确保日志目录存在 - if [ ! -d "$log_dir" ]; then - mkdir -p "$log_dir" 2>/dev/null - if [ $? -ne 0 ]; then - print_warning "无法创建日志目录 $log_dir,使用当前目录" - LOG_FILE="${jar_basename}_${timestamp}.log" - else - LOG_FILE="${log_dir}/${jar_basename}_${timestamp}.log" - fi - else - LOG_FILE="${log_dir}/${jar_basename}_${timestamp}.log" - fi -} - -# 等待进程停止 -wait_for_process_stop() { - local pid="$1" - local timeout="${2:-$STOP_TIMEOUT}" - local count=0 - - while [ $count -lt $timeout ]; do - if ! ps -p $pid > /dev/null 2>&1; then - return 0 - fi - sleep 1 - count=$((count + 1)) - done - return 1 -} - -# 根据昵称匹配JAR文件 -find_jar_by_nickname() { - local nickname="$1" - local found_jars=() - - # 如果昵称是 "all",返回所有JAR文件 - if [ "$nickname" = "all" ]; then - for jar in *.jar; do - if [ -f "$jar" ]; then - found_jars+=("$jar") - fi - done - else - # 模糊匹配JAR文件名 - for jar in *.jar; do - if [ -f "$jar" ] && [[ "$jar" == *"$nickname"* ]]; then - found_jars+=("$jar") - fi - done - fi - - # 输出匹配的JAR文件 - printf '%s\n' "${found_jars[@]}" -} - -# 解析JAR包参数列表 -parse_jar_arguments() { - local args=("$@") - local jar_list=() - local action="" - - # 如果没有参数,使用默认行为 - if [ ${#args[@]} -eq 0 ]; then - action="restart" - PARSED_ACTION="$action" - return - fi - - # 检查第一个参数是否是操作命令 - case "${args[0]}" in - "start"|"stop"|"restart"|"status"|"help"|"-h"|"--help") - action="${args[0]}" - - # 验证操作命令 - if ! validate_user_input "$action" "action"; then - return 1 - fi - - # 如果只有操作命令,使用原有逻辑 - if [ ${#args[@]} -eq 1 ]; then - PARSED_ACTION="$action" - return - fi - # 其余参数作为JAR包昵称 - for ((i=1; i<${#args[@]}; i++)); do - # 验证JAR包昵称输入 - if ! validate_user_input "${args[i]}" "general"; then - return 1 - fi - jar_list+=("${args[i]}") - done - ;; - *) - # 第一个参数不是操作命令,默认为restart操作 - action="restart" - # 所有参数都作为JAR包昵称 - for arg in "${args[@]}"; do - # 验证JAR包昵称输入 - if ! validate_user_input "$arg" "general"; then - return 1 - fi - jar_list+=("$arg") - done - ;; - esac - - # 设置全局变量 - PARSED_ACTION="$action" - PARSED_JARS=("${jar_list[@]}") -} - -# 检查JAR文件是否存在 -check_jar_exists() { - # 如果JAR_NAME为空,自动检测当前目录下的JAR文件 - if [ -z "$JAR_NAME" ]; then - local jar_files=(*.jar) - - # 检查是否存在JAR文件 - if [ ! -e "${jar_files[0]}" ]; then - print_error "当前目录下没有找到JAR文件!" - print_warning "请将JAR文件放在脚本同一目录下,或在脚本中指定JAR_NAME" - return 1 - fi - - # 如果只有一个JAR文件,自动使用 - if [ ${#jar_files[@]} -eq 1 ]; then - JAR_NAME="${jar_files[0]}" - - # 验证JAR文件名的安全性 - if ! validate_jar_name "$JAR_NAME"; then - return 1 - fi - - print_info "自动检测到JAR文件: $JAR_NAME" - else - # 多个JAR文件时,让用户选择 - print_info "检测到多个JAR文件,请选择要启动的应用:" - echo "" - - # 显示选项列表 - local i=1 - for jar in "${jar_files[@]}"; do - echo -e "${GREEN}[$i]${NC} $jar" - ((i++)) - done - echo "" - - # 提示用户输入 - echo -e "${YELLOW}输入选项:${NC}" - echo -e " - 单个序号: ${GREEN}1${NC} (选择第1个JAR包)" - echo -e " - 多个序号: ${GREEN}1,3,5${NC} 或 ${GREEN}1 3 5${NC} (选择多个JAR包)" - echo -e " - 全部选择: ${GREEN}all${NC} (选择所有JAR包)" - echo "" - echo -n -e "${YELLOW}请输入选择: ${NC}" - read -r choice - - # 验证用户输入 - if ! validate_user_input "$choice" "number"; then - return 1 - fi - - # 处理输入 - local selected_jars=() - - if [ "$choice" = "all" ]; then - # 选择所有JAR包 - selected_jars=("${jar_files[@]}") - print_success "已选择所有JAR包 (${#selected_jars[@]}个)" - else - # 解析序号输入(支持逗号分隔和空格分隔) - local numbers - if [[ "$choice" == *","* ]]; then - # 逗号分隔 - IFS=',' read -ra numbers <<< "$choice" - else - # 空格分隔 - read -ra numbers <<< "$choice" - fi - - # 验证并收集选中的JAR包 - local valid_selection=true - for num in "${numbers[@]}"; do - # 去除空格 - num=$(echo "$num" | tr -d ' ') - - if [[ "$num" =~ ^[0-9]+$ ]] && [ "$num" -ge 1 ] && [ "$num" -le ${#jar_files[@]} ]; then - selected_jars+=("${jar_files[$((num-1))]}") - else - print_error "无效的序号 '$num'!请输入1到${#jar_files[@]}之间的数字" - valid_selection=false - break - fi - done - - if [ "$valid_selection" = false ]; then - return 1 - fi - - # 去重 - selected_jars=($(printf '%s\n' "${selected_jars[@]}" | sort -u)) - - print_success "已选择 ${#selected_jars[@]} 个JAR包:" - for jar in "${selected_jars[@]}"; do - echo -e " - $jar" - done - fi - - # 验证所有选中的JAR文件名 - for jar in "${selected_jars[@]}"; do - if ! validate_jar_name "$jar"; then - return 1 - fi - done - - echo "" - - # 如果选择了多个JAR包,设置全局变量供批量操作使用 - if [ ${#selected_jars[@]} -gt 1 ]; then - SELECTED_JARS=("${selected_jars[@]}") - MULTI_JAR_MODE=true - # 设置第一个JAR作为默认JAR_NAME(兼容性) - JAR_NAME="${selected_jars[0]}" - else - # 单个JAR包 - JAR_NAME="${selected_jars[0]}" - MULTI_JAR_MODE=false - fi - fi - fi - - # 检查指定的JAR文件是否存在 - if [ ! -f "$JAR_NAME" ]; then - print_error "JAR文件 '$JAR_NAME' 不存在!" - print_warning "请检查JAR_NAME配置是否正确,或确保JAR文件在当前目录下" - return 1 - fi - - # 检查文件权限 - if ! check_file_permissions "$JAR_NAME"; then - return 1 - fi - - return 0 -} - -# 获取运行中的应用PID -get_app_pid() { - local jar_name="$1" - - if [ -z "$jar_name" ]; then - jar_name="$JAR_NAME" - fi - - # 通过进程名查找(更可靠的方法) - pgrep -f "$jar_name" | head -1 -} - -# 检查应用是否正在运行 -is_running() { - local jar_name="$1" - local pid=$(get_app_pid "$jar_name") - - if [ -n "$pid" ]; then - return 0 # 正在运行 - else - return 1 # 未运行 - fi -} - -# 启动应用 -start_app() { - local jar_name="$1" - - if [ -z "$jar_name" ]; then - jar_name="$JAR_NAME" - fi - - if ! check_java_environment; then - return 1 - fi - - setup_jar_variables "$jar_name" - - if is_running "$jar_name"; then - print_warning "应用 $jar_name 已在运行中 (PID: $(get_app_pid "$jar_name"))" - return 0 - fi - - print_info "正在启动应用: $jar_name" - print_info "日志文件: $LOG_FILE" - - # 启动应用 - nohup java $JAVA_OPTS -jar "$jar_name" > "$LOG_FILE" 2>&1 & - - # 等待启动 - sleep "$STARTUP_WAIT" - - # 验证启动状态 - if is_running "$jar_name"; then - local actual_pid=$(get_app_pid "$jar_name") - print_success "应用 $jar_name 启动成功 (PID: $actual_pid)" - print_info "查看日志: tail -f $LOG_FILE" - return 0 - else - print_error "应用 $jar_name 启动失败" - print_warning "请检查日志文件: $LOG_FILE" - return 1 - fi -} - -# 停止应用 -stop_app() { - local jar_name="$1" - - if [ -z "$jar_name" ]; then - jar_name="$JAR_NAME" - fi - - local pid=$(get_app_pid "$jar_name") - - if [ -z "$pid" ]; then - print_warning "应用 $jar_name 未运行" - return 0 - fi - - setup_jar_variables "$jar_name" - - print_info "正在停止应用 $jar_name (PID: $pid)..." - - # 发送TERM信号 - kill "$pid" 2>/dev/null - - # 等待进程停止 - if wait_for_process_stop "$pid"; then - print_success "应用 $jar_name 已成功停止" - return 0 - else - print_warning "应用 $jar_name 未在 ${STOP_TIMEOUT} 秒内停止,强制终止..." - kill -9 "$pid" 2>/dev/null - sleep "$FORCE_KILL_WAIT" - - # 再次检查 - if [ -z "$(get_app_pid "$jar_name")" ]; then - print_success "应用 $jar_name 已强制停止" - return 0 - else - print_error "无法停止应用 $jar_name" - return 1 - fi - fi -} - -# 重启应用 -restart_app() { - local jar_name="$1" - - if [ -z "$jar_name" ]; then - jar_name="$JAR_NAME" - fi - - if ! check_java_environment; then - return 1 - fi - - setup_jar_variables "$jar_name" - - print_info "正在重启应用: $jar_name" - - if is_running "$jar_name"; then - print_info "正在停止应用..." - stop_app "$jar_name" - else - print_info "应用未运行,直接启动" - fi - - print_info "正在启动应用..." - start_app "$jar_name" -} - -# 显示应用状态 -show_status() { - local jar_name="$1" - - if [ -z "$jar_name" ]; then - jar_name="$JAR_NAME" - fi - - setup_jar_variables "$jar_name" - - print_info "=== 应用状态 ===" - echo "JAR包: $jar_name" - echo "JVM参数: $JAVA_OPTS" - echo "日志文件: $LOG_FILE" - echo "" - - local pid=$(get_app_pid "$jar_name") - - if [ -n "$pid" ]; then - print_success "✓ 应用正在运行 (PID: $pid)" - - # 显示进程信息 - if command -v ps >/dev/null 2>&1; then - echo "" - echo "进程详情:" - ps -p "$pid" -o pid,ppid,cmd --no-headers 2>/dev/null || echo " 无法获取进程详情" - fi - else - print_warning "✗ 应用未运行" - fi -} - -# 批量启动应用 -batch_start_app() { - local jar_files=("$@") - local success_count=0 - local total_count=${#jar_files[@]} - - if [ $total_count -eq 0 ]; then - print_error "没有指定要启动的JAR包" - return 1 - fi - - if ! check_java_environment; then - return 1 - fi - - print_info "=== 批量启动应用 ===" - print_info "准备启动 $total_count 个JAR包..." - echo "" - - for jar_file in "${jar_files[@]}"; do - print_info "正在启动: $jar_file" - - # 检查JAR文件是否存在 - if [ ! -f "$jar_file" ]; then - print_error " JAR文件不存在: $jar_file" - continue - fi - - setup_jar_variables "$jar_file" - - # 检查是否已经在运行 - if is_running "$jar_file"; then - print_warning " $jar_file 已经在运行" - ((success_count++)) - continue - fi - - # 启动应用 - echo " JAR包: $jar_file" - echo " JVM参数: $JAVA_OPTS" - echo " 日志文件: $LOG_FILE" - - nohup java $JAVA_OPTS -jar "$jar_file" > "$LOG_FILE" 2>&1 & - - sleep "$STARTUP_WAIT" - - if is_running "$jar_file"; then - local pid=$(get_app_pid "$jar_file") - print_success " $jar_file 启动成功 (PID: $pid)" - ((success_count++)) - else - print_error " $jar_file 启动失败" - print_warning " 请检查日志文件: $LOG_FILE" - fi - echo "" - done - - print_info "=== 批量启动完成 ===" - if [ $success_count -eq $total_count ]; then - print_success "成功启动: $success_count/$total_count" - else - print_warning "成功启动: $success_count/$total_count" - fi -} - -# 批量停止应用 -batch_stop_app() { - local jar_files=("$@") - local success_count=0 - local total_count=${#jar_files[@]} - - if [ $total_count -eq 0 ]; then - print_error "没有指定要停止的JAR包" - return 1 - fi - - print_info "=== 批量停止应用 ===" - print_info "准备停止 $total_count 个JAR包..." - echo "" - - for jar_file in "${jar_files[@]}"; do - print_info "正在停止: $jar_file" - - local pid=$(get_app_pid "$jar_file") - - if [ -z "$pid" ]; then - print_warning " $jar_file 未运行" - ((success_count++)) - continue - fi - - setup_jar_variables "$jar_file" - - echo " 正在停止进程 (PID: $pid)..." - kill "$pid" 2>/dev/null - - # 等待进程停止 - if wait_for_process_stop "$pid"; then - print_success " $jar_file 停止成功" - ((success_count++)) - else - print_warning " 强制停止进程..." - kill -9 "$pid" 2>/dev/null - sleep "$FORCE_KILL_WAIT" - - if [ -z "$(get_app_pid "$jar_file")" ]; then - print_success " $jar_file 强制停止成功" - ((success_count++)) - else - print_error " $jar_file 停止失败" - fi - fi - echo "" - done - - print_info "=== 批量停止完成 ===" - if [ $success_count -eq $total_count ]; then - print_success "成功停止: $success_count/$total_count" - else - print_warning "成功停止: $success_count/$total_count" - fi -} - -# 批量重启应用 -batch_restart_app() { - local jar_files=("$@") - - if [ ${#jar_files[@]} -eq 0 ]; then - print_error "没有指定要重启的JAR包" - return 1 - fi - - if ! check_java_environment; then - return 1 - fi - - print_info "=== 批量重启应用 ===" - print_info "准备重启 ${#jar_files[@]} 个JAR包..." - echo "" - - # 先停止所有应用 - batch_stop_app "${jar_files[@]}" - - echo "" - sleep "$STARTUP_WAIT" - - # 再启动所有应用 - batch_start_app "${jar_files[@]}" -} - -# 批量显示状态 -batch_show_status() { - local jar_files=("$@") - - if [ ${#jar_files[@]} -eq 0 ]; then - print_error "没有指定要查看状态的JAR包" - return 1 - fi - - print_info "=== 批量应用状态 ===" - echo "" - - local running_count=0 - local total_count=${#jar_files[@]} - - for jar_file in "${jar_files[@]}"; do - echo -e "${CYAN}--- $jar_file ---${NC}" - - setup_jar_variables "$jar_file" - - echo "JAR包: $jar_file" - echo "JVM参数: $JAVA_OPTS" - echo "日志文件: $LOG_FILE" - - local pid=$(get_app_pid "$jar_file") - - if [ -n "$pid" ]; then - print_success "✓ 正在运行 (PID: $pid)" - ((running_count++)) - else - print_warning "✗ 未运行" - fi - echo "" - done - - print_info "=== 状态统计 ===" - if [ $running_count -eq $total_count ]; then - print_success "运行中: $running_count/$total_count (全部运行)" - elif [ $running_count -eq 0 ]; then - print_warning "运行中: $running_count/$total_count (全部停止)" - else - print_warning "运行中: $running_count/$total_count (部分运行)" - fi -} - -# 显示帮助信息 -show_help() { - cat << 'EOF' -Java应用管理脚本 - -用法: - ./restart-java-app.sh {start|stop|restart|status|help} [jar昵称1] [jar昵称2] ... - ./restart-java-app.sh [jar昵称1] [jar昵称2] ... # 默认执行restart操作 - -命令说明: - start - 启动应用 - stop - 停止应用 - restart - 重启应用(默认操作) - status - 查看应用状态 - help - 显示帮助信息 - -JAR包昵称说明: - • 支持模糊匹配:输入JAR包名称的一部分即可匹配 - • 支持多个昵称:可以同时指定多个JAR包昵称进行批量操作 - • 支持 'all' 参数:使用 'all' 可以对所有JAR包进行操作 - • 自动检测:如果不指定昵称,脚本会自动检测当前目录下的JAR包 - -使用示例: - # 单JAR包操作 - ./restart-java-app.sh start # 启动(自动检测JAR包) - ./restart-java-app.sh restart myapp # 重启包含"myapp"的JAR包 - ./restart-java-app.sh status user-service # 查看user-service状态 - - # 多JAR包批量操作 - ./restart-java-app.sh start user order # 启动user和order相关的JAR包 - ./restart-java-app.sh stop all # 停止所有JAR包 - ./restart-java-app.sh restart user order pay # 重启多个服务 - - # 默认重启操作(省略restart命令) - ./restart-java-app.sh myapp # 重启myapp - ./restart-java-app.sh user order # 重启user和order服务 - -配置说明: - • JAR_NAME: JAR文件名(留空自动检测) - • JAVA_OPTS: JVM参数(默认: -Xms512m -Xmx1024m) - • 日志文件: 自动生成为 {jar名称}.log - -注意事项: - • 脚本需要在JAR文件所在目录运行 - • 确保有足够的权限启动/停止进程 - • 日志文件会在同一目录下生成 - • 支持优雅停止,超时后会强制终止进程 -EOF -} - -# 执行命令的统一处理函数 -execute_command() { - local action="$1" - shift - local jar_files=("$@") - - case "$action" in - "start") - if [ ${#jar_files[@]} -gt 1 ]; then - batch_start_app "${jar_files[@]}" - else - start_app "${jar_files[0]}" - fi - ;; - "stop") - if [ ${#jar_files[@]} -gt 1 ]; then - batch_stop_app "${jar_files[@]}" - else - stop_app "${jar_files[0]}" - fi - ;; - "restart") - if [ ${#jar_files[@]} -gt 1 ]; then - batch_restart_app "${jar_files[@]}" - else - restart_app "${jar_files[0]}" - fi - ;; - "status") - if [ ${#jar_files[@]} -gt 1 ]; then - batch_show_status "${jar_files[@]}" - else - show_status "${jar_files[0]}" - fi - ;; - "help"|"-h"|"--help") - show_help - ;; - *) - print_error "未知命令 '$action'" - show_help - exit 1 - ;; - esac -} - -# ======================================== -# 主程序 -# ======================================== - -# 解析命令行参数 -parse_jar_arguments "$@" - -# 如果解析出了JAR包参数,执行批量操作 -if [ ${#PARSED_JARS[@]} -gt 0 ]; then - # 收集所有匹配的JAR文件 - all_jar_files=() - - for nickname in "${PARSED_JARS[@]}"; do - matched_jars=($(find_jar_by_nickname "$nickname")) - - if [ ${#matched_jars[@]} -eq 0 ]; then - print_error "未找到匹配昵称 '$nickname' 的JAR文件" - exit 1 - fi - - # 添加到总列表 - all_jar_files+=("${matched_jars[@]}") - done - - # 去重 - unique_jars=($(printf '%s\n' "${all_jar_files[@]}" | sort -u)) - - print_info "找到 ${#unique_jars[@]} 个JAR文件:" - for jar in "${unique_jars[@]}"; do - echo " - $jar" - done - echo "" - - execute_command "$PARSED_ACTION" "${unique_jars[@]}" -else - # 检查JAR文件是否存在(可能触发交互式选择) - if ! check_jar_exists; then - exit 1 - fi - - # 处理命令行参数 - ACTION="${PARSED_ACTION:-restart}" - - # 检查是否在check_jar_exists中选择了多个JAR包 - if [ "$MULTI_JAR_MODE" = true ] && [ ${#SELECTED_JARS[@]} -gt 0 ]; then - print_info "检测到多JAR包模式,将对 ${#SELECTED_JARS[@]} 个JAR包执行 '$ACTION' 操作" - echo "" - - execute_command "$ACTION" "${SELECTED_JARS[@]}" - else - # 单JAR包操作 - execute_command "$ACTION" "$JAR_NAME" - fi -fi \ No newline at end of file