Java7 ProcessBuilder 类增强

在 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 类

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 目录如下图:

ea841e609f2b32b8576d08cb4d13878e_1723015944019-85511b90-0dd3-4658-ac81-a9808f6a0113_x-oss-process=image%2Fformat%2Cwebp.png

代码如下:

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

ProcessBuilder 增强

在 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();
        }
    }

}

输出如下图:

49f456fe8c9603ad63ef886cf7b459c3_1723018407763-9af9efe7-eaba-4880-bfaf-bd53303cf9e6_x-oss-process=image%2Fformat%2Cwebp.png

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号