You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用ProcessBuilder执行Shell脚本后Java程序挂起问题求助

问题分析与解决方案

核心问题根源

  1. 主程序挂起:process.waitFor()会持续等待子进程(Shell脚本)终止,但你的脚本启动了一个长期运行的Java服务(server.jar nogui),该服务不会自动退出,导致脚本本身也一直处于运行状态,主Java程序因此卡在waitFor()无法继续执行。
  2. 错误日志为空:你先调用redirectError(new File(...))重定向错误流到文件,随后又调用redirectErrorStream(true)将错误流合并到标准输出,后者覆盖了前者的设置,导致error.txt始终无内容。

方案1:让Shell脚本启动后台进程,快速退出

修改Shell脚本,将Java服务放到后台运行,使脚本执行后立即终止,主程序就能正常获取退出状态并继续运行。

修改后的Shell脚本:

#!/bin/bash

echo "Starting..."
cd /path/to/
# 将Java进程后台运行,并重定向其输出到日志文件
java -Xms2G -Xmx2G -jar /path/to/server.jar nogui > server.log 2>&1 &
  • &:让Java进程在后台执行,脚本无需等待它结束,会直接退出。
  • > server.log 2>&1:将Java进程的标准输出和错误输出统一写入server.log,避免干扰主程序,同时方便后续查看服务日志。

同步修正Java代码的错误流配置:

public static void main(String[] args) {
    System.out.println("Starting!");

    File directory = new File("/path/to");
    File startScript = new File(directory, "script.sh");

    try {
        // 直接执行脚本,简化进程层级
        ProcessBuilder processBuilder = new ProcessBuilder(startScript.getAbsolutePath());
        processBuilder.directory(directory);
        // 单独将脚本的错误流写入文件,不合并到标准输出
        processBuilder.redirectError(new File(directory, "error.txt"));
        Process process = processBuilder.start();

        System.out.println("Process start code ran...");

        int exitStatus = process.waitFor();
        
        if (exitStatus == 0) {
            System.out.println("Test ran successfully!");
        } else {
            System.out.println("Something went wrong. Exit status is " + exitStatus);
        }
    } catch (Exception exception) {
        exception.printStackTrace();
    }
}
  • 移除redirectErrorStream(true),确保错误流正确写入error.txt。
  • 直接用脚本路径作为ProcessBuilder命令,减少不必要的进程嵌套。

方案2:主程序不等待子进程终止,同时处理输出

如果需要主程序继续运行,且不阻塞在waitFor(),可以通过线程处理子进程的输出流(避免缓冲区满导致子进程也挂起),主程序可继续执行其他逻辑。

示例Java代码:

public static void main(String[] args) {
    System.out.println("Starting!");

    File directory = new File("/path/to");
    File startScript = new File(directory, "script.sh");

    try {
        ProcessBuilder processBuilder = new ProcessBuilder(startScript.getAbsolutePath());
        processBuilder.directory(directory);
        // 合并错误流到标准输出,统一处理输出内容
        processBuilder.redirectErrorStream(true);
        Process process = processBuilder.start();

        System.out.println("Process start code ran...");

        // 启动线程读取子进程输出,防止缓冲区溢出导致子进程阻塞
        new Thread(() -> {
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    System.out.println("Script output: " + line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();

        // 主程序继续执行后续逻辑
        System.out.println("Main program continues to run...");

        // 后续可通过process.isAlive()检查子进程状态
        // if (process.isAlive()) { ... }

    } catch (Exception exception) {
        exception.printStackTrace();
    }
}

此方案无需修改Shell脚本,主程序不会挂起,同时能实时捕获脚本和Java服务的输出。


额外注意事项

  • 确保Shell脚本有可执行权限:执行chmod +x /path/to/script.sh,避免因权限问题导致执行失败。
  • 若需要Java服务长期稳定运行,建议结合nohup使用,避免终端关闭后进程被终止:
    nohup java -Xms2G -Xmx2G -jar /path/to/server.jar nogui > server.log 2>&1 &
    

内容的提问来源于stack exchange,提问作者Liam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 05:20:29