多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
# Java `ProcessBuilder`教程 原文:http://zetcode.com/java/processbuilder/ Java `ProcessBuilder`教程显示了如何使用`ProcessBuilder`创建操作系统进程。 ## `ProcessBuilder` `ProcessBuilder`用于创建操作系统进程。 其`start()`方法创建具有以下属性的新`Process`实例: * 命令 * 环境 * 工作目录 * 输入来源 * 标准输出和标准错误输出的目标 * `redirectErrorStream` ## `ProcessBuilder`运行程序 用`command()`执行程序。 使用`waitFor()`,我们可以等待过程完成。 `ExecuteProgram.java` ```java package com.zetcode; import java.io.IOException; public class ExecuteProgram { public static void main(String[] args) throws IOException, InterruptedException { var processBuilder = new ProcessBuilder(); processBuilder.command("notepad.exe"); var process = processBuilder.start(); var ret = process.waitFor(); System.out.printf("Program exited with code: %d", ret); } } ``` 该程序执行 Windows 记事本应用。 它返回其退出代码。 ## `ProcessBuilder`命令输出 以下示例执行命令并显示其输出。 `ProcessBuilderEx.java` ```java package com.zetcode; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class ProcessBuilderEx { public static void main(String[] args) throws IOException { var processBuilder = new ProcessBuilder(); processBuilder.command("cal", "2019", "-m 2"); var process = processBuilder.start(); try (var reader = new BufferedReader( new InputStreamReader(process.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { System.out.println(line); } } } } ``` 该示例运行 Linux `cal`命令。 ```java processBuilder.command("cal", "2019", "-m 2"); ``` `command()`执行`cal`程序。 其他参数是程序的选项。 为了在 Windows 机器上运行命令,我们可以使用以下命令:`processBuilder.command("cmd.exe", "/c", "ping -n 3 google.com")`。 ```java var process = processBuilder.start(); ``` `start()`启动了该过程。 ```java try (var reader = new BufferedReader( new InputStreamReader(process.getInputStream()))) { ``` 使用`getInputStream()`方法,我们从流程的标准输出中获取输入流。 ```java February 2019 Su Mo Tu We Th Fr Sa 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 ``` 这是输出。 ## `ProcessBuilder`重定向输出 使用`redirectOutput()`,我们可以重定向流程构建器的标准输出目的地。 `RedirectOutputEx.java` ```java package com.zetcode; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; public class RedirectOutputEx { public static void main(String[] args) throws IOException { var homeDir = System.getProperty("user.home"); var processBuilder = new ProcessBuilder(); processBuilder.command("cmd.exe", "/c", "date /t"); var fileName = new File(String.format("%s/Documents/tmp/output.txt", homeDir)); processBuilder.redirectOutput(fileName); var process = processBuilder.start(); try (var reader = new BufferedReader( new InputStreamReader(process.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { System.out.println(line); } } } } ``` 该程序将构建器的输出重定向到文件。 它运行 Windows `date`命令。 ```java processBuilder.redirectOutput(fileName); ``` 我们将流程构建器的标准输出重定向到文件。 ```java try (var reader = new BufferedReader( new InputStreamReader(process.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { System.out.println(line); } } ``` 现在输出到文件。 ```java $ echo %cd% C:\Users\Jano\Documents\tmp $ more output.txt Thu 02/14/2019 ``` 当前日期已写入`output.txt`文件。 ## `ProcessBuilder`重定向输入和输出 下一个示例同时重定向输入和输出。 `src/resources/input.txt` ```java sky blue steel morning coffee earth forest ``` 这是`input.txt`文件的内容。 `ProcessBuilderRedirectIOEx.java` ```java package com.zetcode; import java.io.File; import java.io.IOException; public class ProcessBuilderRedirectIOEx { public static void main(String[] args) throws IOException { var processBuilder = new ProcessBuilder(); processBuilder.command("cat") .redirectInput(new File("src/resources", "input.txt")) .redirectOutput(new File("src/resources/", "output.txt")) .start(); } } ``` 在程序中,我们将输入从`input.txt`文件重定向到`cat`命令,并将命令的输出重定向到`output.txt`文件。 ## `ProcessBuilder`继承 IO `inheritIO()`将子流程标准 I/O 的源和目的地设置为与当前 Java 流程相同。 `ProcessBuilderInheritIOEx.java` ```java package com.zetcode; import java.io.IOException; public class ProcessBuilderInheritIOEx { public static void main(String[] args) throws IOException, InterruptedException { var processBuilder = new ProcessBuilder(); processBuilder.command("cmd.exe", "/c", "dir"); var process = processBuilder.inheritIO().start(); int exitCode = process.waitFor(); System.out.printf("Program ended with exitCode %d", exitCode); } } ``` 通过继承已执行命令的 IO,我们可以跳过读取步骤。 程序输出项目目录的内容和显示退出代码的消息。 ```java 02/14/2019 04:55 PM <DIR> . 02/14/2019 04:55 PM <DIR> .. 02/19/2019 01:11 PM <DIR> .idea 02/14/2019 04:55 PM <DIR> out 02/14/2019 04:52 PM 433 ProcessBuilderInheritIOEx.iml 02/14/2019 04:53 PM <DIR> src 1 File(s) 433 bytes 5 Dir(s) 157,350,264,832 bytes free Program ended with exitCode 0 ``` 我们同时获得执行的命令和自己的 Java 程序的输出。 ## `ProcessBuilder`环境 `environment()`方法返回流程构建器环境的字符串映射视图。 `ProcessBuilderEnvEx.java` ```java package com.zetcode; public class ProcessBuilderEnvEx { public static void main(String[] args) { var pb = new ProcessBuilder(); var env = pb.environment(); env.forEach((s, s2) -> { System.out.printf("%s %s %n", s, s2); }); System.out.printf("%s %n", env.get("PATH")); } } ``` 该程序显示所有环境变量。 ```java configsetroot C:\WINDOWS\ConfigSetRoot USERDOMAIN_ROAMINGPROFILE LAPTOP-OBKOFV9J LOCALAPPDATA C:\Users\Jano\AppData\Local PROCESSOR_LEVEL 6 USERDOMAIN LAPTOP-OBKOFV9J LOGONSERVER \\LAPTOP-OBKOFV9J JAVA_HOME C:\Users\Jano\AppData\Local\Programs\Java\openjdk-11\ SESSIONNAME Console ... ``` 这是 Windows 上的示例输出。 在下一个程序中,我们定义一个自定义环境变量。 `ProcessBuilderEnvEx2.java` ```java package com.zetcode; import java.io.IOException; public class ProcessBuilderEnvEx2 { public static void main(String[] args) throws IOException { var pb = new ProcessBuilder(); var env = pb.environment(); env.put("mode", "development"); pb.command("cmd.exe", "/c", "echo", "%mode%"); pb.inheritIO().start(); } } ``` 该程序定义一个`mode`变量并在 Windows 上输出。 ```java pb.command("cmd.exe", "/c", "echo", "%mode%"); ``` `%mode%`是 Windows 的环境变量语法; 在 Linux 上,我们使用`$mode`。 ## `ProcessBuilder`目录 `directory()`方法设置流程构建器的工作目录。 `ProcessBuilderDirectoryEx.java` ```java package com.zetcode; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; public class ProcessBuilderDirectoryEx { public static void main(String[] args) throws IOException { var homeDir = System.getProperty("user.home"); var pb = new ProcessBuilder(); pb.command("cmd.exe", "/c", "dir"); pb.directory(new File(homeDir)); var process = pb.start(); try (var reader = new BufferedReader( new InputStreamReader(process.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { System.out.println(line); } } } } ``` 该示例将主目录设置为流程生成器的当前目录。 我们显示主目录的内容。 ```java var homeDir = System.getProperty("user.home"); ``` 我们得到用户的主目录。 ```java pb.command("cmd.exe", "/c", "dir"); ``` 我们定义了一个在 Windows 上执行`dir`程序的命令。 ```java pb.directory(new File(homeDir)); ``` 我们设置流程构建器的目录。 ```java Volume in drive C is Windows Volume Serial Number is 4415-13BB Directory of C:\Users\Jano 02/14/2019 11:48 AM <DIR> . 02/14/2019 11:48 AM <DIR> .. 10/13/2018 08:38 AM <DIR> .android 01/31/2019 10:58 PM 281 .bash_history 12/17/2018 03:02 PM <DIR> .config ... ``` 这是一个示例输出。 ## `ProcessBuilder`非阻塞操作 在下面的示例中,我们创建一个异步过程。 `ProcessBuilderNonBlockingEx.java` ```java package com.zetcode; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; public class ProcessBuilderNonBlockingEx { public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException, IOException { var executor = Executors.newSingleThreadExecutor(); var processBuilder = new ProcessBuilder(); processBuilder.command("cmd.exe", "/c", "ping -n 3 google.com"); try { var process = processBuilder.start(); System.out.println("processing ping command ..."); var task = new ProcessTask(process.getInputStream()); Future<List<String>> future = executor.submit(task); // non-blocking, doing other tasks System.out.println("doing task1 ..."); System.out.println("doing task2 ..."); var results = future.get(5, TimeUnit.SECONDS); for (String res : results) { System.out.println(res); } } finally { executor.shutdown(); } } private static class ProcessTask implements Callable<List<String>> { private InputStream inputStream; public ProcessTask(InputStream inputStream) { this.inputStream = inputStream; } @Override public List<String> call() { return new BufferedReader(new InputStreamReader(inputStream)) .lines() .collect(Collectors.toList()); } } } ``` 该程序创建一个在控制台上运行`ping`命令的进程。 它在`Executors.newSingleThreadExecutor()`方法的帮助下在单独的线程中执行。 ```java processing ping command ... doing task1 ... doing task2 ... Pinging google.com [2a00:1450:4001:825::200e] with 32 bytes of data: Reply from 2a00:1450:4001:825::200e: time=108ms Reply from 2a00:1450:4001:825::200e: time=111ms Reply from 2a00:1450:4001:825::200e: time=112ms Ping statistics for 2a00:1450:4001:825::200e: Packets: Sent = 3, Received = 3, Lost = 0 (0% loss), Approximate round trip times in milli-seconds: Minimum = 108ms, Maximum = 112ms, Average = 110ms ``` 这是输出。 ## `ProcessBuilder`管道操作 管道是一种用于将信息从一个程序进程传递到另一个程序进程的技术。 `ProcessBuilderPipeEx.java` ```java package com.zetcode; import java.io.File; import java.io.IOException; public class ProcessBuilderPipeEx { public static void main(String[] args) throws IOException { var homeDir = System.getProperty("user.home"); var processBuilder = new ProcessBuilder(); processBuilder.command("cmd.exe", "/c", "dir | grep [dD]o"); processBuilder.directory(new File(homeDir)); processBuilder.inheritIO().start(); } } ``` 该示例通过管道(|)将信息从`dir`命令发送到`grep`命令。 ```java Volume in drive C is Windows 11/14/2018 06:57 PM <DIR> .dotnet 02/18/2019 10:54 PM <DIR> Documents 02/17/2019 01:11 AM <DIR> Downloads ``` 这是输出。 在本教程中,我们使用 Java 的`ProcessBuilder`执行 OS 进程。 您可能也对相关教程感兴趣: [Java `Files.walk`教程](/java/fileswalk/)和 [Java 教程](/lang/java/)。