引
java 编程中,特别是一些工具开发过程中,经常会调用系统命令行,如 windowns 上的 CMD ,linux 操作系统上的 sh 等。
分析
java 提供了调用 CMD 或 sh 的 API , 它被封装在 Runtime 类中。其 exec 方法示意如下:
简单使用方法如下:
public static void main(String[] args) throws IOException {
Runtime.getRuntime().exec("command");
}
更多 Runtime.exec 见 java Runtime.exec方法详解
问题
简单使用 Runtime.exec 存在如下问题:
- 线程阻塞 – Runtime.exec 线程阻塞及处理
- 没有返回正确的输出或错误信息
为此特编写了如下工具类
工具类
日常开发中调用命令行后,需要拿到执行结果或错误结果,并且要高效,为此,结合 Java 异步编程经验,封装了如下工具类:
package com.xzbd.common.utils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.commons.lang3.SystemUtils;
public class CommandUtil {
public static String LINUX_CMD = "/bin/sh";
public static String WIN_CMD = "cmd.exe";
// time out
private static long TIME_OUT = 20;
// time out unit
private static TimeUnit TIME_OUT_UNIT = TimeUnit.SECONDS;
public static void main(String[] args) throws IOException {
String result = CommandUtil.runCmd("ls /home/", 12, TimeUnit.SECONDS);
System.out.println(result);
}
public static String runCmd(String command) {
return runCmd(command, TIME_OUT, TIME_OUT_UNIT);
}
public static String runCmd(String command, long time, TimeUnit unit) {
String[] cmds = createCmd(command);
String cmd = String.join(" ", cmds);
System.out.println("CMD: " + cmd);
System.out.println(SystemUtils.LINE_SEPARATOR);
Process pro = null;
try {
pro = Runtime.getRuntime().exec(cmds);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("cmd exec error");
}
ExecutorService executorService = Executors.newFixedThreadPool(2);
Future<String> result = executorService.submit(new ReaderTask(pro.getInputStream()));
Future<String> errResult = executorService.submit(new ReaderTask(pro.getErrorStream()));
String r1 = resultHandle(result, time, unit);
String r2 = resultHandle(errResult, time, unit);
executorService.shutdown();
if (r2 != null && !r2.isBlank())
return r2;
return r1;
}
/**
* 创建cmd
*/
private static String[] createCmd(String command) {
if (SystemUtils.IS_OS_LINUX) {
// linux -c
return new String[] { LINUX_CMD, "-c", command };
} else if (SystemUtils.IS_OS_WINDOWS) {
// win10 /c
return new String[] { WIN_CMD, "/c", command };
} else {
String systemInfo = SystemUtils.OS_NAME + " " + SystemUtils.OS_ARCH + " " + SystemUtils.OS_VERSION;
throw new RuntimeException("OS Not Supported , the current OS is :" + systemInfo);
}
}
/**
* 预留显示效果控制,例如日志
*/
private static String resultHandle(Future<String> result, long time, TimeUnit unit) {
String tmp = null;
try {
tmp = result.get(time, unit);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
return tmp;
}
private static class ReaderTask implements Callable<String> {
private InputStream inputStream;
private StringBuilder queryResult;
public ReaderTask(InputStream inputStream) {
this.inputStream = inputStream;
queryResult = new StringBuilder();
}
@Override
public String call() throws Exception {
BufferedReader bf = new BufferedReader(new InputStreamReader(inputStream));
String line = null;
while ((line = bf.readLine()) != null) {
queryResult.append(line).append(SystemUtils.LINE_SEPARATOR);
}
bf.close();
return queryResult.toString();
}
}
}
其中 SystemUtils 源自 commons-lang3 ,其 mvn 依赖如下:
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
也可以自己实现一个System 工具类,见 java 获取系统信息