java如何执行linux/cmd命令
作者:夏子曦
本文详细介绍了在Java中执行命令行命令的两种方法:Runtime.exec()和ProcessBuilder,包括它们的优缺点、参数传递、流处理、多线程处理、超时控制、跨平台兼容性和乱码问题的解决方案
在Java中执行命令行命令可以通过Runtime.exec()或ProcessBuilder实现。以下是两种方法的详细说明和示例代码:
使用Runtime.exec()
适用于简单场景,但需手动处理输入/输出流。
try {
// 执行命令(参数以数组形式传递,避免空格问题)
Process process = Runtime.getRuntime().exec(new String[]{"ping", "-n", "3", "127.0.0.1"});
// 读取标准输出流
BufferedReader inputReader = new BufferedReader(
new InputStreamReader(process.getInputStream())
);
// 读取错误流
BufferedReader errorReader = new BufferedReader(
new InputStreamReader(process.getErrorStream())
);
// 打印输出和错误信息
String line;
while ((line = inputReader.readLine()) != null) {
System.out.println("输出: " + line);
}
while ((line = errorReader.readLine()) != null) {
System.out.println("错误: " + line);
}
// 等待命令执行完成并获取退出码
int exitCode = process.waitFor();
System.out.println("退出码: " + exitCode);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
使用ProcessBuilder
更灵活,支持错误流合并、工作目录设置等。
try {
// 构建命令及参数
ProcessBuilder pb = new ProcessBuilder();
pb.command("ping", "-n", "3", "127.0.0.1");
// 合并标准输出和错误流(简化处理)
pb.redirectErrorStream(true);
// 设置工作目录(可选)
// pb.directory(new File("path/to/dir"));
// 启动进程
Process process = pb.start();
// 读取输出流
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream())
);
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
// 等待命令结束
int exitCode = process.waitFor();
System.out.println("退出码: " + exitCode);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
关键点说明
参数传递
将命令和参数拆分为字符串数组,避免空格导致的解析错误。
流处理
- 必须读取输出流:否则进程可能因缓冲区满而阻塞。
- 多线程处理:若需同时处理标准输出和错误流,可使用多线程(如示例1)。
- 合并流:通过
redirectErrorStream(true)合并错误流到标准输出,简化代码(如示例2)。
超时控制
使用waitFor(long timeout, TimeUnit unit)(Java 8+)防止无限等待:
if (!process.waitFor(30, TimeUnit.SECONDS)) {
process.destroy(); // 强制终止进程
System.out.println("命令执行超时");
}
跨平台兼容性
- Windows可能需要
cmd /c前缀:
pb.command("cmd", "/c", "dir");
- 根据系统属性调整命令:
String os = System.getProperty("os.name").toLowerCase();
if (os.contains("win")) {
pb.command("cmd", "/c", "mycommand");
} else {
pb.command("sh", "-c", "mycommand");
}
资源释放
在finally块中关闭流或使用Try-with-Resources(需自定义处理)。
乱码
在Java中读取命令行输出时出现中文乱码,通常是因为系统默认编码与命令行输出的编码不一致(例如,Windows的cmd默认使用GBK编码,而程序可能使用UTF-8)。
以下是解决方案及优化后的代码:
原因分析
- Windows命令行编码:默认使用
GBK(对应代码页CP936)。 - Linux/macOS终端编码:通常为
UTF-8。 - Java程序默认编码:可能与系统编码不同(例如,IDE运行时默认
UTF-8)。
解决方案
1.显式指定字符编码
在创建InputStreamReader时,明确指定与命令行输出一致的编码(如Windows下使用GBK)。
// Windows示例:使用GBK编码读取
BufferedReader inputReader = new BufferedReader(
new InputStreamReader(process.getInputStream(), "GBK")
);
// Linux/macOS示例:使用UTF-8
BufferedReader inputReader = new BufferedReader(
new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8)
);
2.自动检测系统编码(推荐)
动态判断操作系统类型,自动选择编码:
import java.nio.charset.Charset;
public class CharsetHelper {
// 获取命令行输出的默认编码(Windows为GBK,其他系统为UTF-8)
public static Charset getConsoleCharset() {
String os = System.getProperty("os.name").toLowerCase();
if (os.contains("win")) {
return Charset.forName("GBK");
} else {
return StandardCharsets.UTF_8;
}
}
}
// 使用示例:
Charset charset = CharsetHelper.getConsoleCharset();
BufferedReader inputReader = new BufferedReader(
new InputStreamReader(process.getInputStream(), charset)
);
完整优化代码示例(以ProcessBuilder为例)
import org.apache.commons.lang3.StringUtils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
public static void main(String[] args) {
//System.out.println("Hello, World!");
RuntimeExec("ping 127.0.0.1");
RuntimeExec2("ping www.baidu.com");
}
public static void RuntimeExec(String cmd) {
try {
if (StringUtils.isBlank(cmd)) {
return;
}
String[] cmds = cmd.split(" ");
// 出去空格
for (int i = 0; i < cmds.length; i++)
cmds[i] = cmds[i].trim();
// 执行命令(参数以数组形式传递,避免空格问题)
Process process = Runtime.getRuntime().exec(cmds); /*, null, new File("C:\\Windows\\System32")*/ //new String[]{"ping", "-n", "3", "127.0.0.1"});
// 读取标准输出流
BufferedReader inputReader = new BufferedReader(
new InputStreamReader(process.getInputStream(), "GB2312")
);
// 读取错误流
BufferedReader errorReader = new BufferedReader(
new InputStreamReader(process.getErrorStream(), "GB2312")
);
// 打印输出和错误信息
String line;
while ((line = inputReader.readLine()) != null) {
System.out.println("out: " + line);
}
while ((line = errorReader.readLine()) != null) {
System.out.println("err: " + line);
}
// 等待命令执行完成并获取退出码
int exitCode = process.waitFor();
System.out.println("out: " + exitCode);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
public static void RuntimeExec2(String cmd) {
try {
if (StringUtils.isBlank(cmd)) {
return;
}
String[] cmds = cmd.split(" ");
// 出去空格
for (int i = 0; i < cmds.length; i++)
cmds[i] = cmds[i].trim();
// 构建命令及参数
ProcessBuilder pb = new ProcessBuilder();
pb.command(cmds);
// 合并标准输出和错误流(简化处理)
pb.redirectErrorStream(true);
// 设置工作目录(可选)
// pb.directory(new File("path/to/dir"));
// 启动进程
Process process = pb.start();
// 读取输出流
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream(),"GB2312")
);
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
// 等待命令结束
int exitCode = process.waitFor();
System.out.println("退出码: " + exitCode);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
其他注意事项
强制命令行使用UTF-8(Windows)
在Windows中,可以通过/U参数让cmd输出UTF-8编码:
pb.command("cmd", "/U", "/c", "chcp 65001 && ping -n 3 127.0.0.1");
此时需将编码设置为UTF-8:
new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8)
验证命令行编码
Windows下运行chcp命令查看活动代码页:
936→GBK65001→UTF-8
根据实际代码页调整编码设置。
统一环境编码
在程序启动时指定JVM编码(如-Dfile.encoding=UTF-8),但需确保与命令行编码一致。
总结
- 简单命令:推荐
ProcessBuilder,代码更简洁。 - 复杂场景(如交互式输入、大量输出):结合多线程处理流,避免阻塞。
- 注意事项:正确处理参数、流、超时及跨平台问题。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
