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分支下在这里插入图片描述

Logo

纵情码海钱塘涌,杭州开发者创新动! 属于杭州的开发者社区!致力于为杭州地区的开发者提供学习、合作和成长的机会;同时也为企业交流招聘提供舞台!

更多推荐