当前位置: 代码网 > it编程>编程语言>Java > Linux启动Java JAR的Shell脚本的流程步骤

Linux启动Java JAR的Shell脚本的流程步骤

2026年09月07日 Java 我要评论
内容摘要:本文以实际 run.sh 为依据,完整解释 java jar 的启动、停止、重启、jvm/代理参数和日志机制,并给出执行命令、分层验收标准与失败边界。重点提醒:打印“启动成功&r

内容摘要:本文以实际 run.sh 为依据,完整解释 java jar 的启动、停止、重启、jvm/代理参数和日志机制,并给出执行命令、分层验收标准与失败边界。重点提醒:打印“启动成功”只代表后台命令已提交,不代表端口监听或应用健康。

#!/bin/sh

##############################
# 【公共配置变量 - 只需修改这里】
##############################
port=8900                    # 服务端口
jar_name="aitools.jar"  # jar包名称
profile="prod"                # 启动环境
log_file="_server.out"       # 日志文件名
xms="512m"                   # 堆内存初始
xmx="1024m"                   # 堆内存最大
metaspace="512m"             # 元空间最大

# 代理开关:false 不开启,true 开启
enable_proxy=true
proxy_host="127.0.0.1"
proxy_port="7890"
non_proxy_hosts="localhost|127.*|[::1]"

# 执行动作:start 启动、stop 停止、restart 重启、logs 查看实时日志、count 统计匹配记录数、search 查询上下文
# 不传参数时默认重启
action="${1:-restart}"

##############################
# 日志相关操作
##############################
show_logs() {
    if [ ! -f "$log_file" ]; then
        echo "错误:日志文件不存在:$log_file"
        exit 1
    fi

    echo "===== 实时日志:$log_file ====="
    tail -f "$log_file"
}

count_log_lines() {
    if [ ! -f "$log_file" ]; then
        echo "错误:日志文件不存在:$log_file"
        exit 1
    fi

    if [ "$#" -eq 0 ]; then
        echo "用法:$0 count <关键字>"
        echo "示例:$0 count error"
        exit 1
    fi

    keyword="$*"
    match_count=$(grep -f -c -- "$keyword" "$log_file")
    echo "日志文件:$log_file"
    echo "查询字符:$keyword"
    echo "匹配记录数:$match_count"
}

search_log() {
    if [ ! -f "$log_file" ]; then
        echo "错误:日志文件不存在:$log_file"
        exit 1
    fi

    if [ "$#" -eq 0 ]; then
        echo "用法:$0 search <关键字>"
        echo "示例:$0 search error"
        exit 1
    fi

    keyword="$*"
    echo "===== 查询日志:$log_file(匹配记录前后各 5 行) ====="
    if ! grep -n -f -c 5 -- "$keyword" "$log_file"; then
        echo "未找到包含字符:$keyword"
    fi
}

show_usage() {
    echo "用法:$0 [start|stop|restart|logs|count <关键字>|search <关键字>]"
    echo "  start                  启动服务"
    echo "  stop                   停止服务"
    echo "  restart                重启服务(默认操作)"
    echo "  logs                   实时查看日志(等同 tail -f $log_file)"
    echo "  count <关键字>         统计日志中包含关键字的记录数"
    echo "  search <关键字>        查看匹配记录前后各 5 行(带行号)"
}

##############################
# 停止服务
##############################
stop_service() {
    echo "===== 停止服务 [端口:$port] ====="
    pid=$(netstat -nlp 2>/dev/null | grep -w "$port" | sed -r 's#.* (.*)/.*#\1#')
    if [ -n "$pid" ]; then
        kill -9 $pid
        echo "已停止进程:$pid"
        sleep 1
    else
        echo "未发现端口 $port 对应的运行进程。"
    fi
}

case "$action" in
    logs|log|tail)
        show_logs
        exit 0
        ;;
    count|grep)
        shift
        count_log_lines "$@"
        exit 0
        ;;
    search)
        shift
        search_log "$@"
        exit 0
        ;;
    stop)
        stop_service
        exit 0
        ;;
    start)
        pid=$(netstat -nlp 2>/dev/null | grep -w "$port" | sed -r 's#.* (.*)/.*#\1#')
        if [ -n "$pid" ]; then
            echo "错误:端口 $port 已被进程 $pid 占用,请先执行:$0 stop"
            exit 1
        fi
        ;;
    restart)
        stop_service
        ;;
    *)
        show_usage
        exit 1
        ;;
esac

##############################
# 组装 jvm 参数
##############################
set -- \
    "-dserver.port=$port" \
    "-xms$xms" \
    "-xmx$xmx" \
    "-xx:maxmetaspacesize=$metaspace" \
    "-xx:+useg1gc"

if [ "$enable_proxy" = "true" ]; then
    set -- "$@" \
        "-dhttp.proxyhost=$proxy_host" \
        "-dhttp.proxyport=$proxy_port" \
        "-dhttps.proxyhost=$proxy_host" \
        "-dhttps.proxyport=$proxy_port" \
        "-dhttp.nonproxyhosts=$non_proxy_hosts"
fi

##############################
# 清空日志并启动
##############################
echo "===== 启动新服务 ====="
> "$log_file"

nohup java "$@" -jar "$jar_name" \
    "--spring.profiles.active=$profile" \
    > "$log_file" 2>&1 &

echo "启动成功!"
echo "===== 实时日志 ====="
tail -f "$log_file"

读者问题与适用范围

这份脚本解决的是单机、单实例 java 服务的进程管理问题:通过一个命令停止占用指定端口的进程,或者启动指定名称的 jar,并把输出写入日志文件。它适合部署目录中已经存在 jar 的 linux 主机,不等同于 systemd、supervisor 或容器编排方案。

本文讨论的源码是:/users/xiaoxiangyuan/documents/快客达/linux启动脚本/改进版/run.sh。脚本中的变量、命令和输出均按当前文件记录;未在该文件中出现的 java 版本、spring boot 版本、健康检查接口和生产部署结果,不作推断。

核心判断与证据边界

脚本的关键价值不只是“执行 java -jar”,而是把端口占用检查、jvm 参数、代理参数和日志重定向集中成一个可重复入口。不过它的“启动成功”只表示 nohup 命令已提交到后台,脚本没有等待端口监听,也没有调用健康检查接口。因此,进程启动、端口监听和应用就绪必须分开验证。

判断源码证据当前能证明什么未覆盖的边界
默认执行重启action="${1:-restart}"(第 21 行)不传参数时先停再启未判断旧进程是否确实退出
按端口停止进程netstat ... grep -w "$port" ...(第 28 行)可尝试取得端口对应 pid解析格式依赖 netstat 输出,可能误匹配或取不到 pid
参数集中配置第 6~18 行与第 62~76 行端口、内存、环境、代理可在脚本顶部修改没有校验参数格式和取值范围
后台写日志第 84~86 行java 输出进入 $log_file未验证 jar 是否存在、java 是否成功启动
启动后跟踪日志第 90 行 tail -f当前终端可持续观察日志脚本会保持前台,需 ctrl+c 结束跟踪

脚本如何把配置变成一次启动

配置层:只修改顶部变量

port=8900
jar_name="aitools.jar"
profile="prod"
log_file="_server.out"
xms="512m"
xmx="1024m"
metaspace="512m"

enable_proxy=true
proxy_host="127.0.0.1"
proxy_port="7890"
non_proxy_hosts="localhost|127.*|[::1]"

action="${1:-restart}"

这些变量形成启动契约:

变量作用示例注意事项
portspring boot 服务端口8900必须与停服检测的端口一致
jar_namejar 文件名或路径aitools.jar相对路径以执行脚本时的当前目录为准
profilespring profileprod通过应用参数传入
log_file标准输出和错误输出文件_server.out启动时会被清空
xms/xmxjvm 初始/最大堆512m/1024m受主机可用内存约束
metaspace元空间上限512m过小可能导致类加载失败
enable_proxy是否附加代理参数true/false只有严格等于 true 才启用

jar_namelog_file 没有转换为脚本所在目录的绝对路径,所以从错误目录执行 ./run.sh 可能出现“找不到 jar”或日志写到意外位置。这是相对路径语义,不是 java 本身的问题。

动作层:start、stop、restart的分支

停止函数的核心代码如下:

stop_service() {
    echo "===== 停止服务 [端口:$port] ====="
    pid=$(netstat -nlp 2>/dev/null | grep -w "$port" | sed -r 's#.* (.*)/.*#\1#')
    if [ -n "$pid" ]; then
        kill -9 $pid
        echo "已停止进程:$pid"
        sleep 1
    else
        echo "未发现端口 $port 对应的运行进程。"
    fi
}

动作契约如下:

命令行为退出条件
./run.sh stop查询 $port,找到 pid 后执行 kill -9,然后退出停止分支显式 exit 0
./run.sh start先检查端口;已占用则报错并退出,否则继续启动只检查端口,不检查 jar 或 java
./run.sh restart先调用 stop_service,再继续组装参数并启动停止后仅等待 1 秒
./run.sh因默认值等同于 restartrestart 相同
其他参数打印用法并退出exit 1

这里的设计取舍很明确:按端口找进程比记录 pid 文件更容易接管已有进程,但依赖 netstat 文本格式;kill -9 能快速终止进程,却不给应用执行优雅关闭、刷盘和释放资源的机会。

jvm 与代理层:用set --构造参数数组

set -- \
    "-dserver.port=$port" \
    "-xms$xms" \
    "-xmx$xmx" \
    "-xx:maxmetaspacesize=$metaspace" \
    "-xx:+useg1gc"

if [ "$enable_proxy" = "true" ]; then
    set -- "$@" \
        "-dhttp.proxyhost=$proxy_host" \
        "-dhttp.proxyport=$proxy_port" \
        "-dhttps.proxyhost=$proxy_host" \
        "-dhttps.proxyport=$proxy_port" \
        "-dhttp.nonproxyhosts=$non_proxy_hosts"
fi

set -- 把当前脚本的位置参数替换成 jvm 参数;"$@" 再把已有参数逐项追加,避免把整串参数错误地合并成一个参数。最终命令中,-d...-x... 位于 -jar 之前,属于 jvm 参数;--spring.profiles.active=prod 位于 jar 之后,属于 spring boot 应用参数。

代理只配置了 http、https 和 http 非代理主机,未配置 socks 代理。non_proxy_hosts 使用 java 系统属性的匹配格式,实际是否覆盖业务域名仍需结合应用发起请求的客户端实现验证。

完整源码:当前run.sh

以下代码与源码文件保持一致,未把建议改动混入其中。

#!/bin/sh

##############################
# 【公共配置变量 - 只需修改这里】
##############################
port=8900                    # 服务端口
jar_name="aitools.jar"  # jar包名称
profile="prod"                # 启动环境
log_file="_server.out"       # 日志文件名
xms="512m"                   # 堆内存初始
xmx="1024m"                   # 堆内存最大
metaspace="512m"             # 元空间最大

# 代理开关:false 不开启,true 开启
enable_proxy=true
proxy_host="127.0.0.1"
proxy_port="7890"
non_proxy_hosts="localhost|127.*|[::1]"

# 执行动作:start 启动、stop 停止、restart 重启;不传参数时默认重启
action="${1:-restart}"

##############################
# 停止服务
##############################
stop_service() {
    echo "===== 停止服务 [端口:$port] ====="
    pid=$(netstat -nlp 2>/dev/null | grep -w "$port" | sed -r 's#.* (.*)/.*#\1#')
    if [ -n "$pid" ]; then
        kill -9 $pid
        echo "已停止进程:$pid"
        sleep 1
    else
        echo "未发现端口 $port 对应的运行进程。"
    fi
}

case "$action" in
    stop)
        stop_service
        exit 0
        ;;
    start)
        pid=$(netstat -nlp 2>/dev/null | grep -w "$port" | sed -r 's#.* (.*)/.*#\1#')
        if [ -n "$pid" ]; then
            echo "错误:端口 $port 已被进程 $pid 占用,请先执行:$0 stop"
            exit 1
        fi
        ;;
    restart)
        stop_service
        ;;
    *)
        echo "用法:$0 [start|stop|restart]"
        exit 1
        ;;
esac

##############################
# 组装 jvm 参数
##############################
set -- \
    "-dserver.port=$port" \
    "-xms$xms" \
    "-xmx$xmx" \
    "-xx:maxmetaspacesize=$metaspace" \
    "-xx:+useg1gc"

if [ "$enable_proxy" = "true" ]; then
    set -- "$@" \
        "-dhttp.proxyhost=$proxy_host" \
        "-dhttp.proxyport=$proxy_port" \
        "-dhttps.proxyhost=$proxy_host" \
        "-dhttps.proxyport=$proxy_port" \
        "-dhttp.nonproxyhosts=$non_proxy_hosts"
fi

##############################
# 清空日志并启动
##############################
echo "===== 启动新服务 ====="
> "$log_file"

nohup java "$@" -jar "$jar_name" \
    "--spring.profiles.active=$profile" \
    > "$log_file" 2>&1 &

echo "启动成功!"
echo "===== 实时日志 ====="
tail -f "$log_file"

可复现的执行与验证闭环

1. 准备目录和权限

run.shaitools.jar 放在同一目录,或把 jar_name 改成可访问路径;然后执行:

cd /path/to/app
chmod +x run.sh
java -version
ls -l run.sh aitools.jar

java -version 证明 java 命令可用,ls 证明脚本依赖的文件存在;二者都通过后,才有资格进入启动验证。

2. 先做无副作用的语法检查

sh -n run.sh

无输出且退出码为 0,只能说明 shell 语法可被解析,不能证明 netstat、jar 或应用配置正确。

3. 执行动作

./run.sh start
./run.sh stop
./run.sh restart

启动或重启后,当前终端会进入 tail -f。按 ctrl+c 只会结束日志跟踪,不会自动停止后台 java 进程;需要停服时另开终端执行 ./run.sh stop

4. 分层确认服务状态

ss -lntp | grep ':8900'
ps -ef | grep '[a]itools.jar'
tail -n 100 _server.out
curl -f http://127.0.0.1:8900/实际健康检查路径

脚本源码没有提供健康检查路径,因此最后一条命令中的路径必须替换为应用真实接口。只有日志无致命异常、java 进程存在、端口已监听,并且健康接口返回成功,才能判断应用基本就绪。

失败路径、风险与改造建议

  1. netstat 不存在或无权限:查询结果为空,脚本可能误报“未发现进程”,随后重复启动。现代发行版可优先使用 ss,或在部署前明确安装 net-tools
  2. pid 解析不可靠sed -r 依赖扩展正则语法,且脚本假设 netstat -nlp 输出包含 pid/程序名。不同系统或容器环境格式变化时,停止逻辑可能失效。
  3. 强制杀进程kill -9 跳过优雅关闭。更稳妥的顺序是先发送 term,等待有限时间,再对仍存活的 pid 使用 kill
  4. 日志被清空> "$log_file" 会在每次启动前截断历史日志。若需要审计或故障回溯,应改用按日期轮转、追加写入或交给 journald。
  5. 没有启动失败回滚:即使 jar 不存在、端口启动失败或配置错误,脚本仍会先打印“启动成功!”,随后 tail -f 继续等待。应检查后台 pid、短暂等待端口,并设置超时和失败退出码。
  6. 相对路径和多实例限制:脚本依赖当前工作目录,且固定一个端口、一个日志文件、一个 jar,不适合并行多实例;多实例应为每个实例拆分配置或引入服务管理器。

取舍与结论

当前脚本适合“人工登录服务器后快速重启单个 java 服务”的场景:配置集中、命令短、日志即时可见。它的代价是进程识别依赖端口文本解析,停止采用不可恢复的强杀,启动结果没有健康确认,日志也会被覆盖。

如果目标只是开发机或低频人工运维,可以保留现状并执行分层验证;如果目标是生产自动恢复、开机自启、优雅停机和可观测性,应迁移到 systemd/supervisor,并把健康检查、超时、日志轮转和权限隔离纳入服务契约。无论是否改造,不能仅凭“启动成功!”这行输出宣称应用已经可用。

以上就是linux启动java jar的shell脚本的流程步骤的详细内容,更多关于linux启动java jar shell脚本的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com