java.lang.Runtime.exec的左膀右臂:流输入和流读取详解

Java提供了Runtime.exec()方法来启动一个新进程。该方法可以返回Process对象,通过该对象可以控制和管理子进程的输入、输出以及错误流。这个方法中的参数是一个字符串,它描述了一个shell命令,应该如何来运行这个新的子进程。

为了更好地使用exec()方法,在使用exec()的时候,我们应该学会:

1.正确处理进程输出

2.合并输出流,正确地处理错误

3.增加进程的交互

4.通常的编程技巧

下面我们将详细讲解一下这个问题。

一. 确定进程输出

一旦启动一个进程,子进程处理输出流,而主进程负责处理输入和错误。通常情况下,这意味着你应该在一个线程中完成一个任务,而其他的线程则负责读取和处理输出,确保没有线程会阻塞等待运行子进程的输出。

输出有时被缓冲,因此要求读取器定期刷新来将缓冲区中的数据充满。下面的代码展示了一个如何启动UNIX进程的例子:

import java.io.*;
public class Test {
  public static void main(String[] args) throws IOException {
    Runtime rt = Runtime.getRuntime();
    Process proc = rt.exec("ls");
    InputStream stdin = proc.getInputStream();
    InputStreamReader isr = new InputStreamReader(stdin);
    BufferedReader br = new BufferedReader(isr);
    String line = null;
    System.out.println("<OUTPUT>");
    while ( (line = br.readLine()) != null)
      System.out.println(line);
    System.out.println("</OUTPUT>");
    int exitVal = proc.waitFor();
    System.out.println("Process exitValue: " + exitVal);
  }
}

注意到我们启动子进程的时候并没有提供输出程序到主进程的管道。相反,子进程的输出流(在这种情况下是ls的输出)被start()方法连接到新的InputStreamReader,并连接可以传递到缓冲的BufferedReader。然后,我们部分读取由子进程生成的结果并将其显示在主进程的STDOUT上。

二. 合并输出流,正确处理错误

Java允许三种不同的标准进程流:

1.输入流:包括子进程对标准输入流(System.in)的使用。

2.输出流:包括子进程对标准输出流(System.out)和标准错误流(System.err)的使用。

3.错误流:该流用于报告错误,这些错误对于诊断问题很有用。

大多数情况下,我们应该为每种类型的流创建单独的进程来处理特定的工作。但也有时候,显式地为子进程指定流已经足够。这种方法特别适合于与系统进程通信的应用程序(例如,爬虫程序或者系统监视器)。

下面的代码演示了如何合并输出流,并分离错误流。

import java.io.*;
public class Test {
  public static void main(String[] args) throws IOException {
    Runtime rt = Runtime.getRuntime();
    String[] cmd = {"ls", "-al"};
    Process proc = rt.exec(cmd);
    InputStream stdin = proc.getInputStream();
    InputStreamReader isr = new InputStreamReader(stdin);
    BufferedReader br = new BufferedReader(isr);
    InputStream stderr = proc.getErrorStream();
    InputStreamReader esr = new InputStreamReader(stderr);
    BufferedReader ebr = new BufferedReader(esr);
    String line = null;
    System.out.println("<OUTPUT>");
    while ( (line = br.readLine()) != null)
      System.out.println(line);
    System.out.println("</OUTPUT>");
    System.out.println("<ERROR>");
    while ( (line = ebr.readLine()) != null)
      System.out.println(line);
    System.out.println("</ERROR>");
    int exitVal = proc.waitFor();
    System.out.println("Process exitValue: " + exitVal);
  }
}

三. 增加进程的交互

下列示例演示了如何启动一个交互式进程,并发送输入到用不同于按下Enter键的方式回答问题。下面的程序会启动iconv,该程序是一个Unix将不同字符编码转换成一种标准字符编码的命令行实用程序。

import java.io.*;
public class Test {
  public static void main(String[] args) throws IOException {
    Runtime rt = Runtime.getRuntime();
    Process proc = rt.exec("iconv -f ISO-8859-1 -t UTF-8");
    OutputStream out = proc.getOutputStream();
    InputStream in = proc.getInputStream();
    BufferedReader reader = new BufferedReader(new InputStreamReader(in));
    PrintWriter writer = new PrintWriter(out);
    String[] messages = {"food", "coat", "car"};
    for (String message : messages) {
      writer.println(message);
      String line = reader.readLine();
      System.out.println(line);
    }
    writer.println("quit");
    writer.flush();
    int exitVal = proc.waitFor();
    System.out.println("Process exitValue: " + exitVal);
  }
}

四. 通常的编程技巧

当使用ProcessBuilder,Runtime.exec()或System()等方法启动另一个的JVM时,也可以被称为Java调用时,我们需要使用Process.waitFor()等方法等待子进程完成。这里给出完整的示例:

import java.io.*;
public class Test {
  public static void main(String[] args) throws IOException, InterruptedException {
    String command = "java -cp ./example.jar com.mycompany.app.Main";
    ProcessBuilder builder = new ProcessBuilder(command.split("\\s+"));
    builder.directory(new File("/home/user/"));
    builder.redirectOutput(new File("/home/user/output.txt"));
    Process process = builder.start();
    int exitValue = process.waitFor();
    if (exitValue == 0) {
      System.out.println("Process exited without errors");
    } else {
      System.out.println("Process exited with errors");
    }
  }
}

在这个示例中,我们使用ProcessBuilder来启动一个Java应用程序,该程序包含在example.jar中,并且该应用程序的Main类位于com.mycompany.app包中。操作目录被设置为/home/user路径。标准输出被重定向到一个名为output.txt的文件。最后在waitFor()方法中等待进程结束,如果进程完全正确执行将返回0,否则返回非0。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java.lang.Runtime.exec的左膀右臂:流输入和流读取详解 - Python技术站

(0)
上一篇 2023年5月26日
下一篇 2023年5月26日

相关文章

  • 谈谈JavaScript自定义回调函数

    谈谈JavaScript自定义回调函数 什么是回调函数? 回调函数是一种特殊的函数,它作为参数传递给另一个函数并且在主函数执行完成后被调用。通常情况下,回调函数用于处理异步操作。比如,当一个网络请求完成时,需要回调函数来处理返回的数据。 JavaScript自定义回调函数的基本用法 在JavaScript中,我们可以通过自定义函数来实现回调函数的功能。下面是…

    Java 2023年6月15日
    00
  • Spring Data Jpa 复杂查询方式总结(多表关联及自定义分页)

    下面就是 Spring Data JPA 复杂查询方式的攻略: 概述 Spring Data JPA 提供 JPA 规范标准的数据访问方式,并简化了持久层的开发。在实际应用场景中,有些查询需要多表关联及自定义分页方式。 本文将介绍 Spring Data JPA 多表关联及自定义分页的实现方式。 多表关联查询 基于 JPA 查询 在 JPA 中,我们可以通过…

    Java 2023年6月2日
    00
  • Java面试题冲刺第三天–集合框架篇

    让我来为您详细讲解“Java面试题冲刺第三天–集合框架篇”的完整攻略。 一、前言 集合框架是Java编程中的重要一环,作为Java工程师,在面试中对集合框架要有深刻的理解。本篇文章将为您提供Java集合框架面试题的完整攻略,帮助您在面试中脱颖而出。 二、集合框架概述 集合框架是Java中的一组接口、实现类和算法,用于存储和操作一组对象。在Java编程中,集…

    Java 2023年5月19日
    00
  • 浅谈@RequestMapping注解的注意点

    浅谈@RequestMapping注解的注意点 @RequestMapping注解是Spring MVC中最常用的注解之一,它用于将HTTP请求映射到控制器方法。在本文中,我们将详细讲解@RequestMapping注解的注意点,并提供两个示例来说明这个过程。 注意点 在使用@RequestMapping注解时,我们需要注意以下几点: value属性 @Re…

    Java 2023年5月18日
    00
  • Java NIO通信基础示例详解

    下面是“Java NIO通信基础示例详解”的完整攻略。 概述 Java NIO是Java 1.4版本引入的一种新的I/O处理方式。相较于传统的I/O方式,NIO采用了非阻塞式I/O模型,使得I/O的效率更高。本文将详细讲解Java NIO通信的基础知识和实现方式。 NIO简介 NIO是New IO的缩写,它是用来替代传统的Java IO的。Java IO(流…

    Java 2023年5月26日
    00
  • Java技能点之SimpleDateFormat进行日期格式化问题

    下面是Java技能点之SimpleDateFormat进行日期格式化问题的完整攻略。 简介 SimpleDateFormat是Java SE自带的日期时间格式化工具,可以用来将日期时间类型的数据按照指定格式输出。SimpleDateFormat支持多种格式化输出,如输出年月日、输出时分秒、输出星期几等。 使用方法 1. 创建SimpleDateFormat对…

    Java 2023年5月20日
    00
  • spring boot教程之产生的背景及其优势

    Spring Boot教程之产生的背景及其优势 产生背景 在传统的Java Web开发过程中,我们需要编写大量的配置文件,比如web.xml、spring.xml等。而随着技术的不断发展,Java Web开发过程中出现了很多新的框架,比如Spring、Spring MVC、Hibernate等。但是这些框架的集成配置却比较麻烦,需要编写大量XML配置文件。因…

    Java 2023年5月15日
    00
  • application作用域实现用户登录挤掉之前登录用户代码

    首先我们需要明确一下“application作用域”和“用户登录挤掉之前登录用户”的概念。 “application作用域”:指整个web应用程序都能够访问的作用域,存储的数据是全局共享的,任何用户访问该应用程序都可以访问这些数据。可以通过以下代码获取application作用域对象: ServletContext application = request…

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部