Java程序在服务器上执行脚本命令 - 详解

Java程序在服务器上执行脚本命令

核心技术与依赖

  • Java 标准库:主要使用了java.lang.ProcessBuilder和java.io包,这是 Java 原生用于创建操作系统进程和处理输入输出的 API
  • 跨平台支持:通过判断操作系统类型(Windows/Linux)执行不同的命令解释器
  • 异常处理:自定义异常抛出和捕获机制
  • 字符编码处理:使用StandardCharsets.UTF_8处理输出,避免中文乱码

整体工具类

package com.ruoyi.shell;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.rmi.ServerException;
import java.util.LinkedHashMap;
import java.util.Map;
public class ShellUtils {
/**
* 获取当前系统对应脚本执行对象
*
* @param scriptPath 脚本路径
* @return 脚本执行对象
*/
public static ProcessBuilder getScriptExecute(String scriptPath) throws IOException {
String os = System.getProperty("os.name").toLowerCase();
// 系统判断
if (os.contains("win")) {
return new ProcessBuilder("cmd.exe", "/c", scriptPath);
} else {
return new ProcessBuilder("/usr/bin/bash", "-c", scriptPath);
}
}
/**
* 执行脚本并返回结果
*
* @param scriptPath 脚本路径
* @return exitCode
* @throws IOException
* @throws InterruptedException
*/
public static Integer excuteScript(String scriptPath)  {
try {
ProcessBuilder scriptExecute = getScriptExecute(scriptPath);
// 执行脚本
Process process = scriptExecute.start();
// 等待脚本执行完毕,并返回执行结果
return process.waitFor();
}catch (Exception e){
e.printStackTrace();
throw new RuntimeException("指令执行错误");
}
}
public static Map<String, Object> findAjaxResult(String command) throws IOException, InterruptedException {
  Map<String, Object> map = new LinkedHashMap<>();
    /** 进程获取 */
    String os = System.getProperty("os.name").toLowerCase();
    ProcessBuilder processBuilder;
    if (os.contains("win")) {
    processBuilder = new ProcessBuilder("cmd.exe", "/c", command);
    } else {
    processBuilder = new ProcessBuilder("/bin/bash", "-c", command);
    }
    // 合并错误流到输出流,避免进程阻塞(重要)
    processBuilder.redirectErrorStream(true);
    // 启动进程
    Process process = processBuilder.start();
    // 获取命令输出(包括标准输出和错误输出)
    BufferedReader reader = new BufferedReader(
    new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8) // 指定编码,避免中文乱码
    );
    StringBuilder output = new StringBuilder();
    String line;
    // 读取输出
    while ((line = reader.readLine()) != null) {
    output.append(line).append("\n");
    }
    // 关闭流
    reader.close();
    // 等待命令执行完成
    int exitCode = process.waitFor();
    // 将输出内容和退出码都存入map
    map.put("exitCode", exitCode);
    map.put("output", output.toString().trim()); // 存入输出内容,trim()去除多余空行
    // 根据命令执行结果返回相应信息
    if (exitCode != 0) {
    throw new ServerException("运行失败,退出码:" + exitCode + ",错误信息:" + output.toString().trim());
    }
    return map;
    }
    }

具体方法

  1. getScriptExecute方法
  • 作用:根据操作系统类型,返回对应的脚本执行器
  • 关键逻辑:
    通过System.getProperty(“os.name”)获取操作系统名称
    Windows 系统使用cmd.exe /c执行脚本
    其他系统(主要是 Linux)使用/usr/bin/bash -c执行脚本
    返回值:配置好的ProcessBuilder对象,用于后续执行脚本

2.excuteScript方法

  • 作用:执行指定路径的脚本,并返回执行结果码
  • 关键逻辑:
    调用getScriptExecute获取适合当前系统的 ProcessBuilder
    通过start()方法启动进程执行脚本
    调用waitFor()阻塞当前线程,直到脚本执行完成
    返回进程的退出码(0 通常表示成功,非 0 表示失败)
    异常处理:捕获所有异常并转换为RuntimeException抛出
  1. findAjaxResult方法
  • 作用:执行单个命令(而非脚本文件),并返回包含执行结果的详细信息
  • 关键逻辑:
    创建进程构建器:根据操作系统类型创建对应命令解释器的 ProcessBuilder
    合并错误流:redirectErrorStream(true)将错误输出合并到标准输出,避免进程阻塞
    启动进程:通过start()方法执行命令
    处理输出:
    使用BufferedReader读取命令输出
    指定 UTF-8 编码避免中文乱码
    将输出内容存入StringBuilder
    等待完成:process.waitFor()等待命令执行完成并获取退出码
    结果封装:将退出码和输出内容存入LinkedHashMap
    错误处理:如果退出码非 0,抛出ServerException

使用

  1. 脚本
#!/bin/bash
# 这是一个简单的测试脚本
echo "Hello from test script"
exit 0
  1. shell脚本执行结果
    在这里插入图片描述
  2. 代码执行
/**
* 测试两种执行方法
* @throws IOException
* @throws InterruptedException
*/
@Test
public void testExcuteScriptAndFindAjaxResult() throws IOException, InterruptedException {
//1.excuteScript
//执行脚本,只返回执行结果,不反回内容
Integer i = ShellUtils.excuteScript("src/test/resources/test_script.sh");
System.out.println(i);
//2.findAjaxResult
//执行脚本,返回执行结果、返回内容,以map形式
Map<String, Object> ajaxResult = ShellUtils.findAjaxResult("src/test/resources/test_script.sh");
  System.out.println(ajaxResult);
  }

结果如图:
在这里插入图片描述

注意:我是使用mac进行的测试,win上估计会有细微差异

具体参见仓库dev分支下在这里插入图片描述

posted @ 2025-11-07 12:11  clnchanpin  阅读(80)  评论(0)    收藏  举报