Java
Python前端运维数据库
Java
Java
  • 新特性
    • Record
    • Optional
  • 面向对象
    • 面向对象基础
    • 构造方法
    • 继承与多态
    • 接口
    • 修饰符
    • 代码块
    • 接口(Interface)
    • 枚举类
  • IO流
    • IO
      • 字节流
      • 字符流
      • 缓冲流
      • 转换流
      • 操作ZIP
      • File 对象
    • NIO
      • Channel和Buffer
      • 异步文件通道AsynchronousFileChannel
      • Selector
      • Path/Files/Pipe
  • 反射
  • 内存分配
  • 集合
    • 简介
    • List
    • Set
    • Map
    • EnumMap
  • 日期与时间
    • Date和Calendar
    • Java8 新时间 ✨
      • LocalDateTime
      • ZonedDateTime
      • Duration
    • 时间格式化
      • SimpleDateFromat
      • DateTimeFormatter ✨
    • Instant
    • 实践
  • 网络编程
    • IP 地址
    • 网络模型
    • TCP 编程
    • UDP 编程
    • HTTP 编程
  • 加密和安全
  • 并发编程
    • 多线程
    • 线程与进程的区别
    • 线程组和线程优先级
    • 线程池
    • 线程锁
  • 异步任务
    • Future
    • CompletableFuture
      • 开启异步任务
      • 串行任务方法
      • 并行任务方法
      • 任务结束方法
      • 异常处理方法
      • 查看状态方法
      • 设置任务结果方法
  • 执行系统命令
  • Stream 流
    • Stream 流的创建
    • Stream 流串行与并行
    • Stream 流中间操作
    • Stream 流终端操作
  • Lambda 表达式
    • Lambda 表达式简介
    • Lambda 表达式语法
    • 方法引用
  • String
  • StringBuffer
由 GitBook 提供支持
在本页
  • processBuilder
  • 工作目录
  • 环境变量
  • 重定向输入和输出
  • 重定向输入
  • 重定向输出
  • 继承IO
  • 管道操作
  • ​超时与终止
  • 异步处理

这有帮助吗?

执行系统命令

processBuilder

ProcessBuilder processBuilder = new ProcessBuilder();
processBuilder.command("ping", "-c", "5", "www.baidu.com");
processBuilder.redirectErrorStream(true);
Process process = processBuilder.start();

// 向子进程写入数据
try (OutputStream outputStream = process.getOutputStream()) {
    outputStream.write("Hello, world!\n".getBytes());
    outputStream.flush();
}
try (InputStream inputStream = process.getInputStream();
     BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
}
int exitCode = process.waitFor();
System.out.println("Exit code: " + exitCode);

工作目录

使用directory方法可以修改子进程默认的工作目录。默认情况下,当前工作目录设置为user.dir系统属性返回的值。

ProcessBuilder processBuilder = new ProcessBuilder(commandList);   
processBuilder.directory(new File("/home/var"));

环境变量

它使用System.getenv() 但作为 Map 返回当前进程环境的副本。下面的示例中,演示了如何获取当前环境变量,以及修改环境变量并传入子进程中。

  • 获取当前环境变量

ProcessBuilder processBuilder = new ProcessBuilder(commandList);
Map<String, String> environment = processBuilder.environment();
environment.forEach((k, v) -> System.out.println(k + "=" + v));
  • 添加一个环境变量

ProcessBuilder processBuilder = new ProcessBuilder(commandList);
Map<String, String> environment = processBuilder.environment();
environment.put("k", "v");

重定向输入和输出

重定向输入

var processBuilder = new ProcessBuilder();
processBuilder.command("cat")
        .redirectInput(new File("src/main/resources", "input.txt"))
        .redirectOutput(new File("src/main/resources/", "output.txt"))
        .start();

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

重定向输出

​ProcessBuilder processBuilder = new ProcessBuilder(commandList);

processBuilder.redirectOutput(ProcessBuilder.Redirect.INHERIT);  // 将标准输出重定向到父进程
processBuilder.redirectOutput(ProcessBuilder.Redirect.DISCARD);  // 将标准输出重定向到标准输出
processBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE);  // 将标准输出重定向到管道
processBuilder.redirectOutput(new File("./output.log"));  // 将标准输出重定向到文件
processBuilder.redirectOutput(ProcessBuilder.Redirect.appendTo(new File("./output.txt")));  // 将标准输出重定向到文件

可以指定 INFO 和 ERROR 日志输出到不同的文件。

// INFO 日志
processBuilder.redirectOutput(new File("./output.log"));
// ERROR 日志
processBuilder.redirectError(new File("./error.log"));

继承IO

这个示例中,将看到 inheritIO() 方法的作用。当我们想将子进程的 I/O 重定向到当前进程的标准 I/O 时,可以使用这个方法:

ProcessBuilder processBuilder = new ProcessBuilder(commandList);

processBuilder.redirectErrorStream(true);
// 把子线程 I/O 输出重定向当前进程
processBuilder.inheritIO();

Process process = processBuilder.start();

管道操作

从 Java9 开始,ProcessBuilder 引入了管道的概念,可以把一个进程的输出作为另外一个进程的输入再次操作。

public static List<Process> startPipeline(List<ProcessBuilder> builders)

使用这个方法我们进行例如这样的操作:ls -l | wc -l,列出文件目录,然后统计行数。

ProcessBuilder lsProcessBuilder = new ProcessBuilder("/bin/bash", "-c", "ls -l");
ProcessBuilder wcProcessBuilder = new ProcessBuilder("wc", "-l");
List<Process> processes = ProcessBuilder.startPipeline(Arrays.asList(lsProcessBuilder, wcProcessBuilder));
Process process = processes.getLast();
System.out.println("pid:" + process.pid());
System.out.println("exitCode:" + process.waitFor());

​超时与终止

如果某个进程在指定的时间段内没有结束,那么可以认为该进程超时了,我们可以通过 destroyForcibly 来杀死该进程。

List<String> commandList = List.of("bash", "-c", "for i in {1..10}; do if (( $RANDOM % 2 )); then echo \"这是标准输出信息 $i\"; else >&2 echo \"这是标准错误信息 $i\"; fi; done");
ProcessBuilder processBuilder = new ProcessBuilder(commandList);
Process process = processBuilder.start();

// 等待一定的时间
boolean waitFor = process.waitFor(3, TimeUnit.SECONDS);

// 若未退出,杀死子进程
if (!waitFor) {
    process.destroyForcibly();  // 用于杀死子进程
    process.waitFor();
    System.out.println("杀死子进程");
}

异步处理

在很多情况下,执行一个命令启动一个新线程后,我们不想阻塞等待进程完成,想要异步华,在进程执行完成后进行通知回调。这时可以使用 CompletableFuture 来实现这个功能。

ProcessBuilder processBuilder = new ProcessBuilder("ping", "-c", "10", "www.baidu.com");
processBuilder.inheritIO(); // 把子线程 I/O 输出重定向到父线程
CompletableFuture.supplyAsync(() -> {
    Process process = null;
    try {
        process = processBuilder.start();
        process.waitFor();
    } catch (IOException | InterruptedException e) {
        throw new RuntimeException(e);
    }
    return null;
}).thenAccept(result -> {
    System.out.println("进程执行结束");
});
Thread.sleep(14 * 1000);
上一页设置任务结果方法下一页Stream 流

最后更新于3个月前

这有帮助吗?