ProcessBuilder是一个final类,Process是一个抽象类。ProcessBuilder.start()
和 Runtime.exec()
方法都被用来创建一个操作系统进程(执行命令行操作),并返回 Process
子类的一个实例,该实例可用来控制进程状态并获得相关信息。
每个进程生成器ProcessBuilder对象管理这些进程属性:
Runtime.exec()
可接受一个单独的字符串,这个字符串是通过空格来分隔可执行命令程序和参数的;也可以接受字符串数组参数/list
。ProcessBuilder.start()
只支持字符串数组参数。
创建的子进程没有自己的终端或控制台。它的所有标准 io(即 stdin,stdout,stderr)操作都将通过三个流(getOutputStream(),getInputStream(),getErrorStream()) 重定向到父进程。
// Runtime.exec最终是通过调用ProcessBuilder来真正执行操作的
public Process exec(String[] cmdarray, String[] envp, File dir)
throws IOException {
// 在 directory() 指定的工作目录中,利用 environment() 指定的进程环境,新进程将调用由 command() 给出的命令和参数。
return new ProcessBuilder(cmdarray)
.environment(envp)
.directory(dir)
.start();
}
{"cmd", "/c"}
或 {"/bin/bash", "-c"}
。现有如下三种解决方法,缓冲区内容消费掉即可。
// Do not let external processes block on I|O streams
// 场景一: 使用java.lang.ProcessBuilder.redirectErrorStream(boolean redirectErrorStream)方法即可清空流
ProcessBuilder builder = new ProcessBuilder(cmds);
builder.redirectErrorStream(true);
try {
process = builder.start();
} catch (IOException e) {
e.pringtStackTrace();
}
// 场景二:当出现IOException异常时不应该将IOException异常throws,使用try/catch对IOException单独捕获
Process process = null;
try {
process = builder.start();
} catch (IOException e) {
e.pringtStackTrace();
}
String handleMessage = "";
BufferedReader bufferedReader = new BufferedSReader(new InputStreamReader(process.getInputStream, StandardCharesets.UTF_8));
try {
while ((handleMessage = bufferedReader.readLine()) != null) {
System.out.println(handleMessage);
}
} catch (IOException e) {
e.pringtStackTrace();
}
try {
bufferedReader.close();
} catch (IOException e) {
e.pringtStackTrace();
}
// 场景三:有时候我们可能需要调用系统外部的某个程序,此时就可以用Runtime.getRuntime().exec()来调用,他会生成一个新的进程去运行调用的程序,waitFor()方法也有很明显的弊端,因为java程序给进程的输出流分配的缓冲区是很小的,有时候当进程输出信息很大的时候回导致缓冲区被填满,如果不及时处理程序会阻塞,解决的方法就是处理缓冲区中的信息,开两个线程分别去处理标准输出流和错误输出流
Process process = Runtime.getRuntime().exec(str);
// 记录进程缓存错误信息
final StringBuffer errorLog = new StringBuffer();
final InputStream errorStream = process.getErrorStream();
final InputStream inputStream = process.getInputStream();
// 处理InputStream的线程
new Thread() {
@Override
public void run() {
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream));
String line = null;
try {
// 消费掉缓存中的数据
while ((line = in.readLine()) != null && !errorLog.toString().contains("ERROR")) {
if (line != null) {
errorLog.append(line);
}
}
} catch (IOException e) {
// public RuntimeException(String message, Throwable cause)
throw new RuntimeException("[shell exec error]:" + errorLog, e);
} finally {
try {
inputStream.close();;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
// 处理errorStream的线程
new Thread() {
@Override
public void run() {
BufferedReader err = new BufferedReader(new InputStreamReader(errorStream));
String line = null;
try {
// 消费掉缓存中的数据
while ((line = err.readLine()) != null && !errorLog.toString().contains("ERROR")) {
if (line != null) {
errorLog.append(line);
}
}
} catch (IOException e) {
throw new RuntimeException("[shell exec error]:" + errorLog, e);
} finally {
try {
errorStream.close();;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
logger.info("等待shell脚本执行完成");
Thread.sleep(1000);
// 异常终止
if (errorLog != null && errorLog.length() > 0 && errorLog.toString().contains("ERROR")) {
dispatchLogger.error("[shell exec error]:" + errorLog);
throw new RuntimeException("[shell exec error]:" + errorLog);
}
// 等待shell脚本执行完成
process.waitFor();