1. Runtime
使用Runtime方法执行外部命令
// 获取当前Runtime
public static Runtime getRuntime();
// 执行命令,如"ping baidu.com"
public Process exec(String command) throws IOException;
// 将命令拆成数组形式,如{"ping", "baidu.com"}
public Process exec(String cmdarray[]) throws IOException;
// 执行命令,指定环境变量(name=value形式)
public Process exec(String command, String[] envp) throws IOException;
// 执行命令,指定环境变量、工作目录(默认继承当前进程的工作目录)
public Process exec(String command, String[] envp, File dir) throws IOException;
2. ProcessBuilder
也是使用ProcessBuilder执行
// 指定命令
public ProcessBuilder command(String... command);
// 不能指定环境变量,只能通过Runtime.exec
ProcessBuilder environment(String[] envp);
// 指定工作目录
public ProcessBuilder directory(File directory);
// 创建子进程执行
public Process start() throws IOException;
3. Process
进程接口。Runtime.exec()方法创建子进程,通过返回的Process实例获取执行情况。
3.1 输入输出
子进程没有自己的终端和控制台。它的所有标准 io(即 stdin,stdout,stderr)操作都将通过三个流 (getOutputStream(),getInputStream(),getErrorStream()) 重定向到父进程。父进程使用这些流来提供到子进程的输入和获得从子进程的输出。
因为有些本机平台仅针对标准输入和输出流提供有限的缓冲区大小,如果读写子进程的输出流或输入流出现失败,则可能导致子进程阻塞,甚至产生死锁。
// 向子进程提供stdin
public abstract OutputStream getOutputStream();
// 读取子进程stdout
public abstract InputStream getInputStream();
// 读取子进程stderr
public abstract InputStream getErrorStream();
也可以使用ProcessBuilder重定向子进程的标准IO。
// 重定向stdin
public ProcessBuilder redirectInput(File file);
// 重定向stdout
public ProcessBuilder redirectOutput(File file);
// 重定向stderr
public ProcessBuilder redirectError(File file);
3.2 执行状态
// 阻塞等待子进程执行完毕
public abstract int waitFor() throws InterruptedException;
// 超时等待子进程执行完毕
public boolean waitFor(long timeout, TimeUnit unit) throws InterruptedException;
// 非阻塞获取子进程退出值,0表示正常结束
// 若子进程并没有终止将抛出IllegalThreadStateException
public abstract int exitValue();
// 非阻塞检查子进程存活状态
public boolean isAlive();
3.3 终止进程
public abstract void destroy();
public Process destroyForcibly();
4. 示例
4.1 执行ping
// 指定命令
Process process = Runtime.getRuntime().exec("ping baidu.com");
// 阻塞等待
process.waitFor();
// 打印退出值
System.out.println(process.exitValue());
4.2 获取进程执行输出
先定义一个通用输入流处理函数:
private static final Consumer<InputStream> consumer = (InputStream inputStream) -> {
BufferedReader bufferedReader = null;
try {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "gbk"));
String line = null;
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
改造程序:
Process process = Runtime.getRuntime().exec("ping baidu.com");
consumer.accept(process.getInputStream());
process.waitFor();
System.out.println(process.exitValue());
结果如图:
image.png
4.3 并发读取输出流和错误流
由于输出流和错误流缓冲区空间有限,如果不及时消费,会导致写操作阻塞,进程夯住。
hang.sh:写stdout和stderr
#!/bin/bash
for i in {1..1000}
do
echo "stdout hang................" >&1
done
for i in {1..1000}
do
echo "stderr hang................" >&2
done
对于stdout和stderr都有写入的情况,如果串行读取或只读取一个,会导致进程夯住。
Process process = Runtime.getRuntime().exec("bash D:\\Temp\\Process\\hang.sh");
consumer.accept(process.getInputStream());
consumer.accept(process.getErrorStream());
process.waitFor();
System.out.println(process.exitValue());
需要对流并发读取:
Process process = Runtime.getRuntime().exec("bash D:\\Temp\\Process\\hang.sh");
new Thread(() -> consumer.accept(process.getInputStream())).start();
new Thread(() -> consumer.accept(process.getErrorStream())).start();
process.waitFor();
System.out.println(process.exitValue());
4.4 重定向标准流
ProcessBuilder processBuilder = new ProcessBuilder();
processBuilder.directory(new File("D:\\Temp\\Process")); // 设置工作目录
processBuilder.command("bash", "redirect.sh"); // 必须分开写
processBuilder.redirectInput(new File("D:\\Temp\\Process\\stdin.log")); // 重定向stdin
processBuilder.redirectOutput(new File("D:\\Temp\\Process\\stdout.log")); // 重定向stdout
processBuilder.redirectError(new File("D:\\Temp\\Process\\stderr.log")); // 重定向stderr
Process process = processBuilder.start();
process.waitFor();
脚本如下:从stdin分别读取5行,写入stdout和stderr。
#!/bin/bash
for i in {1..5}
do
read line
echo -e "$line\n" >&1
done
for i in {1..5}
do
read line
echo -e "$line\n" >&2
done
网友评论