Java ProcessBuilder
教程
原文:http://zetcode.com/java/processbuilder/
Java ProcessBuilder
教程显示了如何使用ProcessBuilder
创建操作系统进程。
ProcessBuilder
ProcessBuilder
用于创建操作系统进程。 其start()
方法创建具有以下属性的新Process
实例:
- 命令
- 环境
- 工作目录
- 输入来源
- 标准输出和标准错误输出的目标
redirectErrorStream
ProcessBuilder
运行程序
用command()
执行程序。 使用waitFor()
,我们可以等待过程完成。
ExecuteProgram.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
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
命令。
processBuilder.command("cal", "2019", "-m 2");
command()
执行cal
程序。 其他参数是程序的选项。 为了在 Windows 机器上运行命令,我们可以使用以下命令:processBuilder.command("cmd.exe", "/c", "ping -n 3 google.com")
。
var process = processBuilder.start();
start()
启动了该过程。
try (var reader = new BufferedReader(
new InputStreamReader(process.getInputStream()))) {
使用getInputStream()
方法,我们从流程的标准输出中获取输入流。
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
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
命令。
processBuilder.redirectOutput(fileName);
我们将流程构建器的标准输出重定向到文件。
try (var reader = new BufferedReader(
new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
现在输出到文件。
$ echo %cd%
C:\Users\Jano\Documents\tmp
$ more output.txt
Thu 02/14/2019
当前日期已写入output.txt
文件。
ProcessBuilder
重定向输入和输出
下一个示例同时重定向输入和输出。
src/resources/input.txt
sky
blue
steel
morning
coffee
earth
forest
这是input.txt
文件的内容。
ProcessBuilderRedirectIOEx.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
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,我们可以跳过读取步骤。 程序输出项目目录的内容和显示退出代码的消息。
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
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"));
}
}
该程序显示所有环境变量。
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
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 上输出。
pb.command("cmd.exe", "/c", "echo", "%mode%");
%mode%
是 Windows 的环境变量语法; 在 Linux 上,我们使用$mode
。
ProcessBuilder
目录
directory()
方法设置流程构建器的工作目录。
ProcessBuilderDirectoryEx.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);
}
}
}
}
该示例将主目录设置为流程生成器的当前目录。 我们显示主目录的内容。
var homeDir = System.getProperty("user.home");
我们得到用户的主目录。
pb.command("cmd.exe", "/c", "dir");
我们定义了一个在 Windows 上执行dir
程序的命令。
pb.directory(new File(homeDir));
我们设置流程构建器的目录。
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
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()
方法的帮助下在单独的线程中执行。
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
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
命令。
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 教程。