Java执行系统命令时经常遇到的问题
标准输入流与错误输入流控制
对待两股输入流常见的控制方式主要包括三种:
(1)开启两个线程分别处理两股输入流,设置状态标识位,主线程轮训判断是否两股输入流均读取完成,在均完成读取的情况下,获取命令执行结果数据。
首先,建立一个处理输入流的线程,参考如下代码:
private static class InputStreamReaderThread extends Thread {
private InputStream in;
private ArrayList<Byte> result = new ArrayList<Byte>();
public boolean isEnd;//判断流是否读取完毕
public InputStreamReaderThread(InputStream in) {
this.in = in;
}
// 获取命令执行后输出结果信息
protected String getResult() throws UnsupportedEncodingException {
byte[] bytes = new byte[result.size()];
for (int i = 0; i < result.size(); i++) {
bytes[i] = ((Byte) result.get(i)).byteValue();
}
return new String(bytes, "gb2312");
}
public void run() {
try {
int readInt = in.read();
while (readInt != -1) {
result.add(Byte.valueOf(String.valueOf((byte) readInt)));
readInt = in.read();
}
isEnd = true;
} catch (Exception e) {
e.printStackTrace();
}
}
}
主线程采用轮训的方式,判断两股流是否读取完毕,完毕后,读取结果信息,参考如下代码:
String result[] = new String[2];
InputStreamReaderThread msgReaderThread = new InputStreamReaderThread(process.getInputStream());
InputStreamReaderThread errReaderThread = new InputStreamReaderThread(process.getErrorStream());
msgReaderThread.start();
errReaderThread.start();
while (true) {
if (msgReaderThread.isEnd && errReaderThread.isEnd) {
break;
} else {
Thread.sleep(1000);
}
}
result[0] = msgReaderThread.getResult();
result[1] = errReaderThread.getResult();
(2)通过waitFor阻塞,直至命令执行完后通过waitFor返回值(命令执行return 值)判断使用哪种输入流读取结果数据。
参考如下代码:
Process p = Runtime.getRuntime().exec(command);
BufferedReader reader = null;
if(p.waitFor() == 0){
reader = new BufferedReader(new InputStreamReader(
p.getInputStream()));
} else {
reader = new BufferedReader(new InputStreamReader(
p.getErrorStream()));
}
(3)使用ProcessBuilder设置合并两股流,这种方法也是本篇文章下文所给出的解决方法。
命令持续输出执行结果
如ping -t 127.0.0.1这样的命令程序会持续一直产生输出流,需要在读取输入流时不间断地读取,并适时判断命令是否执行结束。
推荐参考实现
一个记录结果的数据结构
public class ProccessRunningResult {
private String feedback = null;
private int exitValue;
public int getExitValue() {
return exitValue;
}
public void setExitValue(int exitValue) {
this.exitValue = exitValue;
}
public String getFeedback() {
return feedback;
}
public void setFeedback(String feedback) {
this.feedback = feedback;
}
public void appendFeedback(String feedback){
this.feedback += feedback;
}
}
主程序实现
import java.io.BufferedInputStream;
import java.io.IOException;
import java.lang.ProcessBuilder.Redirect;
import java.util.concurrent.TimeUnit;
public class OSUtils {
private static String encode = null;
//获取系统中文字符集
static {
encode = System.getProperty("sun.jnu.encoding");
}
/**
* 读取一次输入流
* @param in 输入流
* @param sb 保存对象
* @throws IOException
*/
private static void read(BufferedInputStream in, StringBuffer sb) throws IOException{
byte[] buffer = new byte[in.available()];
if( in.read(buffer) != -1){
String feedback = new String(buffer, encode);
System.out.println(feedback);
sb.append(feedback);
}
}
/**
* 执行系统命令
* @param command 命令及参数
* @return
*/
public static ProccessRunningResult runSystemCommand(String... command) {
Process p = null;
BufferedInputStream in = null;
int exitValue;
ProccessRunningResult prr = new ProccessRunningResult();
StringBuffer sb = new StringBuffer();
try {
ProcessBuilder pb = new ProcessBuilder(command);
pb.redirectErrorStream(true);//错误输入流与标准输入流合并
pb.redirectOutput(Redirect.PIPE);//输入流重定向使用管道方式
p = pb.start();
in = (BufferedInputStream)p.getInputStream();
while(true){
read(in, sb);
if(p.waitFor(1, TimeUnit.SECONDS)){//一旦被执行程序退出,将余下的输入流读完,并设置结果和退出状态数据
read(in, sb);
prr.setFeedback(sb.toString());
exitValue = p.exitValue();
prr.setExitValue(exitValue);
break;
}
}
} catch(Exception e){
e.printStackTrace();
} finally {
p.destroy();
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return prr;
}
}
使用方法
ProccessRunningResult result = OSUtils.runSystemCommand("ping","-t", "127.0.0.1");