原文:http://zetcode.com/java/processbuilder/

Java ProcessBuilder教程显示了如何使用ProcessBuilder创建操作系统进程。

ProcessBuilder

ProcessBuilder用于创建操作系统进程。 其start()方法创建具有以下属性的新Process实例:

  • 命令
  • 环境
  • 工作目录
  • 输入来源
  • 标准输出和标准错误输出的目标
  • redirectErrorStream

ProcessBuilder运行程序

command()执行程序。 使用waitFor(),我们可以等待过程完成。

ExecuteProgram.java

  1. package com.zetcode;
  2. import java.io.IOException;
  3. public class ExecuteProgram {
  4. public static void main(String[] args) throws IOException, InterruptedException {
  5. var processBuilder = new ProcessBuilder();
  6. processBuilder.command("notepad.exe");
  7. var process = processBuilder.start();
  8. var ret = process.waitFor();
  9. System.out.printf("Program exited with code: %d", ret);
  10. }
  11. }

该程序执行 Windows 记事本应用。 它返回其退出代码。

ProcessBuilder命令输出

以下示例执行命令并显示其输出。

ProcessBuilderEx.java

  1. package com.zetcode;
  2. import java.io.BufferedReader;
  3. import java.io.IOException;
  4. import java.io.InputStreamReader;
  5. public class ProcessBuilderEx {
  6. public static void main(String[] args) throws IOException {
  7. var processBuilder = new ProcessBuilder();
  8. processBuilder.command("cal", "2019", "-m 2");
  9. var process = processBuilder.start();
  10. try (var reader = new BufferedReader(
  11. new InputStreamReader(process.getInputStream()))) {
  12. String line;
  13. while ((line = reader.readLine()) != null) {
  14. System.out.println(line);
  15. }
  16. }
  17. }
  18. }

该示例运行 Linux cal命令。

  1. processBuilder.command("cal", "2019", "-m 2");

command()执行cal程序。 其他参数是程序的选项。 为了在 Windows 机器上运行命令,我们可以使用以下命令:processBuilder.command("cmd.exe", "/c", "ping -n 3 google.com")

  1. var process = processBuilder.start();

start()启动了该过程。

  1. try (var reader = new BufferedReader(
  2. new InputStreamReader(process.getInputStream()))) {

使用getInputStream()方法,我们从流程的标准输出中获取输入流。

  1. February 2019
  2. Su Mo Tu We Th Fr Sa
  3. 1 2
  4. 3 4 5 6 7 8 9
  5. 10 11 12 13 14 15 16
  6. 17 18 19 20 21 22 23
  7. 24 25 26 27 28

这是输出。

ProcessBuilder重定向输出

使用redirectOutput(),我们可以重定向流程构建器的标准输出目的地。

RedirectOutputEx.java

  1. package com.zetcode;
  2. import java.io.BufferedReader;
  3. import java.io.File;
  4. import java.io.IOException;
  5. import java.io.InputStreamReader;
  6. public class RedirectOutputEx {
  7. public static void main(String[] args) throws IOException {
  8. var homeDir = System.getProperty("user.home");
  9. var processBuilder = new ProcessBuilder();
  10. processBuilder.command("cmd.exe", "/c", "date /t");
  11. var fileName = new File(String.format("%s/Documents/tmp/output.txt", homeDir));
  12. processBuilder.redirectOutput(fileName);
  13. var process = processBuilder.start();
  14. try (var reader = new BufferedReader(
  15. new InputStreamReader(process.getInputStream()))) {
  16. String line;
  17. while ((line = reader.readLine()) != null) {
  18. System.out.println(line);
  19. }
  20. }
  21. }
  22. }

该程序将构建器的输出重定向到文件。 它运行 Windows date命令。

  1. processBuilder.redirectOutput(fileName);

我们将流程构建器的标准输出重定向到文件。

  1. try (var reader = new BufferedReader(
  2. new InputStreamReader(process.getInputStream()))) {
  3. String line;
  4. while ((line = reader.readLine()) != null) {
  5. System.out.println(line);
  6. }
  7. }

现在输出到文件。

  1. $ echo %cd%
  2. C:\Users\Jano\Documents\tmp
  3. $ more output.txt
  4. Thu 02/14/2019

当前日期已写入output.txt文件。

ProcessBuilder重定向输入和输出

下一个示例同时重定向输入和输出。

src/resources/input.txt

  1. sky
  2. blue
  3. steel
  4. morning
  5. coffee
  6. earth
  7. forest

这是input.txt文件的内容。

ProcessBuilderRedirectIOEx.java

  1. package com.zetcode;
  2. import java.io.File;
  3. import java.io.IOException;
  4. public class ProcessBuilderRedirectIOEx {
  5. public static void main(String[] args) throws IOException {
  6. var processBuilder = new ProcessBuilder();
  7. processBuilder.command("cat")
  8. .redirectInput(new File("src/resources", "input.txt"))
  9. .redirectOutput(new File("src/resources/", "output.txt"))
  10. .start();
  11. }
  12. }

在程序中,我们将输入从input.txt文件重定向到cat命令,并将命令的输出重定向到output.txt文件。

ProcessBuilder继承 IO

inheritIO()将子流程标准 I/O 的源和目的地设置为与当前 Java 流程相同。

ProcessBuilderInheritIOEx.java

  1. package com.zetcode;
  2. import java.io.IOException;
  3. public class ProcessBuilderInheritIOEx {
  4. public static void main(String[] args) throws IOException, InterruptedException {
  5. var processBuilder = new ProcessBuilder();
  6. processBuilder.command("cmd.exe", "/c", "dir");
  7. var process = processBuilder.inheritIO().start();
  8. int exitCode = process.waitFor();
  9. System.out.printf("Program ended with exitCode %d", exitCode);
  10. }
  11. }

通过继承已执行命令的 IO,我们可以跳过读取步骤。 程序输出项目目录的内容和显示退出代码的消息。

  1. 02/14/2019 04:55 PM <DIR> .
  2. 02/14/2019 04:55 PM <DIR> ..
  3. 02/19/2019 01:11 PM <DIR> .idea
  4. 02/14/2019 04:55 PM <DIR> out
  5. 02/14/2019 04:52 PM 433 ProcessBuilderInheritIOEx.iml
  6. 02/14/2019 04:53 PM <DIR> src
  7. 1 File(s) 433 bytes
  8. 5 Dir(s) 157,350,264,832 bytes free
  9. Program ended with exitCode 0

我们同时获得执行的命令和自己的 Java 程序的输出。

ProcessBuilder环境

environment()方法返回流程构建器环境的字符串映射视图。

ProcessBuilderEnvEx.java

  1. package com.zetcode;
  2. public class ProcessBuilderEnvEx {
  3. public static void main(String[] args) {
  4. var pb = new ProcessBuilder();
  5. var env = pb.environment();
  6. env.forEach((s, s2) -> {
  7. System.out.printf("%s %s %n", s, s2);
  8. });
  9. System.out.printf("%s %n", env.get("PATH"));
  10. }
  11. }

该程序显示所有环境变量。

  1. configsetroot C:\WINDOWS\ConfigSetRoot
  2. USERDOMAIN_ROAMINGPROFILE LAPTOP-OBKOFV9J
  3. LOCALAPPDATA C:\Users\Jano\AppData\Local
  4. PROCESSOR_LEVEL 6
  5. USERDOMAIN LAPTOP-OBKOFV9J
  6. LOGONSERVER \\LAPTOP-OBKOFV9J
  7. JAVA_HOME C:\Users\Jano\AppData\Local\Programs\Java\openjdk-11\
  8. SESSIONNAME Console
  9. ...

这是 Windows 上的示例输出。

在下一个程序中,我们定义一个自定义环境变量。

ProcessBuilderEnvEx2.java

  1. package com.zetcode;
  2. import java.io.IOException;
  3. public class ProcessBuilderEnvEx2 {
  4. public static void main(String[] args) throws IOException {
  5. var pb = new ProcessBuilder();
  6. var env = pb.environment();
  7. env.put("mode", "development");
  8. pb.command("cmd.exe", "/c", "echo", "%mode%");
  9. pb.inheritIO().start();
  10. }
  11. }

该程序定义一个mode变量并在 Windows 上输出。

  1. pb.command("cmd.exe", "/c", "echo", "%mode%");

%mode%是 Windows 的环境变量语法; 在 Linux 上,我们使用$mode

ProcessBuilder目录

directory()方法设置流程构建器的工作目录。

ProcessBuilderDirectoryEx.java

  1. package com.zetcode;
  2. import java.io.BufferedReader;
  3. import java.io.File;
  4. import java.io.IOException;
  5. import java.io.InputStreamReader;
  6. public class ProcessBuilderDirectoryEx {
  7. public static void main(String[] args) throws IOException {
  8. var homeDir = System.getProperty("user.home");
  9. var pb = new ProcessBuilder();
  10. pb.command("cmd.exe", "/c", "dir");
  11. pb.directory(new File(homeDir));
  12. var process = pb.start();
  13. try (var reader = new BufferedReader(
  14. new InputStreamReader(process.getInputStream()))) {
  15. String line;
  16. while ((line = reader.readLine()) != null) {
  17. System.out.println(line);
  18. }
  19. }
  20. }
  21. }

该示例将主目录设置为流程生成器的当前目录。 我们显示主目录的内容。

  1. var homeDir = System.getProperty("user.home");

我们得到用户的主目录。

  1. pb.command("cmd.exe", "/c", "dir");

我们定义了一个在 Windows 上执行dir程序的命令。

  1. pb.directory(new File(homeDir));

我们设置流程构建器的目录。

  1. Volume in drive C is Windows
  2. Volume Serial Number is 4415-13BB
  3. Directory of C:\Users\Jano
  4. 02/14/2019 11:48 AM <DIR> .
  5. 02/14/2019 11:48 AM <DIR> ..
  6. 10/13/2018 08:38 AM <DIR> .android
  7. 01/31/2019 10:58 PM 281 .bash_history
  8. 12/17/2018 03:02 PM <DIR> .config
  9. ...

这是一个示例输出。

ProcessBuilder非阻塞操作

在下面的示例中,我们创建一个异步过程。

ProcessBuilderNonBlockingEx.java

  1. package com.zetcode;
  2. import java.io.BufferedReader;
  3. import java.io.IOException;
  4. import java.io.InputStream;
  5. import java.io.InputStreamReader;
  6. import java.util.List;
  7. import java.util.concurrent.Callable;
  8. import java.util.concurrent.ExecutionException;
  9. import java.util.concurrent.Executors;
  10. import java.util.concurrent.Future;
  11. import java.util.concurrent.TimeUnit;
  12. import java.util.concurrent.TimeoutException;
  13. import java.util.stream.Collectors;
  14. public class ProcessBuilderNonBlockingEx {
  15. public static void main(String[] args) throws InterruptedException,
  16. ExecutionException, TimeoutException, IOException {
  17. var executor = Executors.newSingleThreadExecutor();
  18. var processBuilder = new ProcessBuilder();
  19. processBuilder.command("cmd.exe", "/c", "ping -n 3 google.com");
  20. try {
  21. var process = processBuilder.start();
  22. System.out.println("processing ping command ...");
  23. var task = new ProcessTask(process.getInputStream());
  24. Future<List<String>> future = executor.submit(task);
  25. // non-blocking, doing other tasks
  26. System.out.println("doing task1 ...");
  27. System.out.println("doing task2 ...");
  28. var results = future.get(5, TimeUnit.SECONDS);
  29. for (String res : results) {
  30. System.out.println(res);
  31. }
  32. } finally {
  33. executor.shutdown();
  34. }
  35. }
  36. private static class ProcessTask implements Callable<List<String>> {
  37. private InputStream inputStream;
  38. public ProcessTask(InputStream inputStream) {
  39. this.inputStream = inputStream;
  40. }
  41. @Override
  42. public List<String> call() {
  43. return new BufferedReader(new InputStreamReader(inputStream))
  44. .lines()
  45. .collect(Collectors.toList());
  46. }
  47. }
  48. }

该程序创建一个在控制台上运行ping命令的进程。 它在Executors.newSingleThreadExecutor()方法的帮助下在单独的线程中执行。

  1. processing ping command ...
  2. doing task1 ...
  3. doing task2 ...
  4. Pinging google.com [2a00:1450:4001:825::200e] with 32 bytes of data:
  5. Reply from 2a00:1450:4001:825::200e: time=108ms
  6. Reply from 2a00:1450:4001:825::200e: time=111ms
  7. Reply from 2a00:1450:4001:825::200e: time=112ms
  8. Ping statistics for 2a00:1450:4001:825::200e:
  9. Packets: Sent = 3, Received = 3, Lost = 0 (0% loss),
  10. Approximate round trip times in milli-seconds:
  11. Minimum = 108ms, Maximum = 112ms, Average = 110ms

这是输出。

ProcessBuilder管道操作

管道是一种用于将信息从一个程序进程传递到另一个程序进程的技术。

ProcessBuilderPipeEx.java

  1. package com.zetcode;
  2. import java.io.File;
  3. import java.io.IOException;
  4. public class ProcessBuilderPipeEx {
  5. public static void main(String[] args) throws IOException {
  6. var homeDir = System.getProperty("user.home");
  7. var processBuilder = new ProcessBuilder();
  8. processBuilder.command("cmd.exe", "/c", "dir | grep [dD]o");
  9. processBuilder.directory(new File(homeDir));
  10. processBuilder.inheritIO().start();
  11. }
  12. }

该示例通过管道(|)将信息从dir命令发送到grep命令。

  1. Volume in drive C is Windows
  2. 11/14/2018 06:57 PM <DIR> .dotnet
  3. 02/18/2019 10:54 PM <DIR> Documents
  4. 02/17/2019 01:11 AM <DIR> Downloads

这是输出。

在本教程中,我们使用 Java 的ProcessBuilder执行 OS 进程。 您可能也对相关教程感兴趣: Java Files.walk教程Java 教程