在 Java5 之前,Runtime.exec() 方法是在 Java 应用程序中执行一个外部命令的唯一方式。例如:
package com.hxstrive.jdk7.process_builder; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; /** * ProcessBuilder 类 * @author hxstrive.com */ public class ProcessBuilderDemo1 { public static void main(String[] args) { try { Runtime runtime = Runtime.getRuntime(); Process process = runtime.exec("cmd /c dir"); // 可以在这里处理进程的输入流、输出流和错误流 BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "GBK")); String line; while ((line = reader.readLine()) != null) { System.out.println(line); } reader.close(); } catch (IOException e) { e.printStackTrace(); } } }
但是,在 Java5 添加了 ProcessBuilder 类,加强了对操作系统进程的控制。尤其是通过 ProcessBuilder 类你可以改变工作目录。
ProcessBuilder 类是 Java 中用于创建和管理操作系统进程的一个重要工具。
ProcessBuilder 类的主要作用是方便地设置进程的属性,如工作目录、环境变量等,并启动新的进程。
ProcessBuilder 类在需要与外部进程进行交互、执行系统命令等场景中非常有用,能够提供更灵活和可控的方式来管理进程的创建和执行。
以下是 ProcessBuilder 类的一些关键特性和方法:
构造函数:
ProcessBuilder():创建一个默认的 ProcessBuilder 对象。
ProcessBuilder(List<String> command):使用给定的命令列表创建 ProcessBuilder 对象。
属性设置方法:
directory(File directory):设置新进程的工作目录。
environment(Map<String, String> env):设置新进程的环境变量。
启动进程的方法:
start():启动新进程,并返回一个 Process 对象,用于与新进程进行交互。
示例,通过 ProcessBuilder 运行 cmd 命令,在 C:/Temp 目录下执行 dir 命令,列出当前目录的文件信息。如果 C:/Temp 目录如下图:
代码如下:
package com.hxstrive.jdk7.process_builder; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; /** * ProcessBuilder 类 * @author hxstrive.com */ public class ProcessBuilderDemo2 { public static void main(String[] args) { try { ProcessBuilder processBuilder = new ProcessBuilder("cmd", "/c", "dir"); // 设置工作目录为 C:\Temp processBuilder.directory(new File("C:\\Temp")); // 启动进程 Process process = processBuilder.start(); // 可以在这里处理进程的输入流、输出流和错误流 BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "GBK")); String line; while ((line = reader.readLine()) != null) { System.out.println(line); } // 等待进程结束 int exitCode = process.waitFor(); System.out.println("进程退出码: " + exitCode); reader.close(); } catch (Exception e) { e.printStackTrace(); } } }
输出信息如下:
驱动器 C 中的卷是 Windows 卷的序列号是 749F-0001 C:\Temp 的目录 2024/08/07 15:32 <DIR> . 2024/08/07 15:32 <DIR> .. 2024/08/07 15:32 0 test.txt 1 个文件 0 字节 2 个目录 55,762,178,048 可用字节 进程退出码: 0
在 Java7 中,为 ProcessBuilder 添加了一些新的方法,使得开发人员能够方便地将进程的标准输入、输出及错误流重定向到文件中:
ProcessBuilder.Redirect redirectError() 返回此 ProcessBuilder 的标准错误目标。
ProcessBuilder redirectError(File file) 将此 ProcessBuilder 的标准错误目标设置为文件。
ProcessBuilder redirectError(ProcessBuilder.Redirect destination) 设置此 ProcessBuilder 的标准错误目标。
boolean redirectErrorStream() 指示此 ProcessBuilder 是否合并标准错误和标准输出。
ProcessBuilder redirectErrorStream(boolean redirectErrorStream) 设置此 ProcessBuilder 的 redirectErrorStream 属性。
ProcessBuilder.Redirect redirectInput() 返回此 ProcessBuilder 的标准输入源。
ProcessBuilder redirectInput(File file) 将此 ProcessBuilder 的标准输入源设置为文件。
ProcessBuilder redirectInput(ProcessBuilder.Redirect source) 设置此 ProcessBuilder 的标准输入源。
ProcessBuilder.Redirect redirectOutput() 返回此 ProcessBuilder 的标准输出目标。
ProcessBuilder redirectOutput(File file) 将此 ProcessBuilder 的标准输出目标设置为文件。
ProcessBuilder redirectOutput(ProcessBuilder.Redirect destination) 设置此 ProcessBuilder 的标准输出目标。
例如:如果在 C:\\Temp\input.txt 文件存在如下内容:
Yesterday, all my troubles seemed so far away Now it looks as though they're here to stay Oh, I believe in yesterday Suddenly, I'm not half the man I used to be There's a shadow hanging over me Oh, yesterday came suddenly
使用 ProcessBuilder 执行 findstr 和 type 命令进行过滤和输出过滤内容:
package com.hxstrive.jdk7.process_builder; import java.io.BufferedReader; import java.io.File; import java.io.InputStreamReader; /** * ProcessBuilder 类 * @author hxstrive.com */ public class ProcessBuilderDemo3 { public static void main(String[] args) { try { // 1.执行 findstr 命令从输入中将包含 “believe” 的行输出到 output.txt 文件 ProcessBuilder processBuilder = new ProcessBuilder("cmd", "/c", "findstr", "believe"); // 设置工作目录为 C:\Temp processBuilder.directory(new File("C:\\Temp")); // 重定向输入到 input.txt 文件 processBuilder.redirectInput(new File("C:\\Temp\\input.txt")); // 重定向输出到 output.txt 文件 processBuilder.redirectOutput(new File("C:\\Temp\\output.txt")); // 启动进程 Process process = processBuilder.start(); System.out.println("进程退出码: " + process.waitFor()); // 2.执行 type 命令获取 output.txt 文件的内容并输出 processBuilder = new ProcessBuilder("cmd", "/c", "type", "C:\\Temp\\output.txt"); process = processBuilder.start(); BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "GBK")); String line; while ((line = reader.readLine()) != null) { System.out.println(line); } // 等待进程结束 int exitCode = process.waitFor(); System.out.println("进程退出码: " + exitCode); reader.close(); } catch (Exception e) { e.printStackTrace(); } } }
输出结果:
进程退出码: 0 Oh, I believe in yesterday 进程退出码: 0
注意:从 Java8 开始,Process 类提供了一个带有超时时间的 waitFor 方法,定义如下:
boolean waitFor(long timeout, TimeUnit unit) 必要时,触发当前线程等待,直到该进程对象所代表的子进程终止或指定的等待时间结束。
用法示例:
boolean completed = process.waitFor(1,TimeUnit.MINUTES);
Java7 中,还新增的另一个方法是 ProcessBuilder 的 inheritIO 方法。方法定义如下:
ProcessBuilder inheritIO() 将子进程标准 I/O 的源和目标设置为与当前 Java 进程相同。inheritIO() 是一个方便的方法,如果执行:
processBuilder.inheritIO()
执行效果与下面调用的效果完全相同:
processBuilder.redirectInput(Redirect.INHERIT) .redirectOutput(Redirect.INHERIT) .redirectError(Redirect.INHERIT)
这使得 ProcessBuilder 的行为等同于大多数操作系统命令解释器。
例如,运行如下代码,将直接在控制台输出“Hello World”字符串:
package com.hxstrive.jdk7.process_builder; /** * ProcessBuilder 类 * @author hxstrive.com */ public class ProcessBuilderDemo4 { public static void main(String[] args) { try { // 直接在控制台输出 “"Hello World"” ProcessBuilder processBuilder = new ProcessBuilder("cmd", "/c", "echo", "Hello World"); // 关注这里,继承标准输入输出流 processBuilder.inheritIO(); // 启动进程 processBuilder.start().waitFor(); } catch (Exception e) { e.printStackTrace(); } } }
输出如下图: