Java程序在服务器上执行脚本命令
本文介绍了Java程序在服务器上执行脚本命令的核心技术方案。通过Java标准库中的ProcessBuilder类实现跨平台脚本执行,可自动识别Windows/Linux系统并调用对应的命令解释器(cmd/bash)。工具类ShellUtils提供了三个核心方法:1)获取适合当前系统的脚本执行器;2)执行脚本并返回状态码;3)执行命令并返回详细结果(包括输出内容和退出码)。方案特别处理了字符编码问题
·
核心技术与依赖
- 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;
}
}
具体方法
- getScriptExecute方法
- 作用:根据操作系统类型,返回对应的脚本执行器
- 关键逻辑:
通过System.getProperty(“os.name”)获取操作系统名称
Windows 系统使用cmd.exe /c执行脚本
其他系统(主要是 Linux)使用/usr/bin/bash -c执行脚本
返回值:配置好的ProcessBuilder对象,用于后续执行脚本
2.excuteScript方法
- 作用:执行指定路径的脚本,并返回执行结果码
- 关键逻辑:
调用getScriptExecute获取适合当前系统的 ProcessBuilder
通过start()方法启动进程执行脚本
调用waitFor()阻塞当前线程,直到脚本执行完成
返回进程的退出码(0 通常表示成功,非 0 表示失败)
异常处理:捕获所有异常并转换为RuntimeException抛出
- findAjaxResult方法
- 作用:执行单个命令(而非脚本文件),并返回包含执行结果的详细信息
- 关键逻辑:
创建进程构建器:根据操作系统类型创建对应命令解释器的 ProcessBuilder
合并错误流:redirectErrorStream(true)将错误输出合并到标准输出,避免进程阻塞
启动进程:通过start()方法执行命令
处理输出:
使用BufferedReader读取命令输出
指定 UTF-8 编码避免中文乱码
将输出内容存入StringBuilder
等待完成:process.waitFor()等待命令执行完成并获取退出码
结果封装:将退出码和输出内容存入LinkedHashMap
错误处理:如果退出码非 0,抛出ServerException
使用
- 脚本
#!/bin/bash
# 这是一个简单的测试脚本
echo "Hello from test script"
exit 0
- shell脚本执行结果
- 代码执行
/**
* 测试两种执行方法
* @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分支下
更多推荐
所有评论(0)