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

 更新时间:2021年11月03日 11:45:05   作者:Morpheus丶  
这篇文章主要介绍了java.lang.Runtime.exec的左膀右臂:流输入和流读取详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

在java.lang.Runtime.exec的使用中,我们经常会用到将重定向命令执行的输入/结果或者将错误信息读取出来.

那么,在使用过程中,我们如何正确的使用呢?

什么是java.lang.Runtime

首先我们要明确一点,什么是Java.lang.Runtime? 我们来看官方[->link<-]的描述:

" Every Java application has a single instance of class Runtime that allows the application to interface with the environment in which the application is running. The current runtime can be obtained from the getRuntime method.

An application cannot create its own instance of this class. "

也就是说,Runtime是每个java-application运行时有且仅有一个的当前实例.允许Application接入当前运行环境.

我们再来看看Runtime的exec()方法:

" Executes the specified command in a separate process. "

这个方法可以让我们在一个进程中执行指定的命令.其返回类型是Process类.

那么我们还需要来看一下Process类:

什么是java.lang.Process

什么是Java.lang.Process? 我们来看官方[->link<-]的描述:

"The class Process provides methods for performing input from the process, performing output to the process, waiting for the process to complete, checking the exit status of the process, and destroying (killing) the process."

也就是说这个类提供控制线程的方法.

我们再来看看Process提供的获取输入流和输出流的方法:

public abstract InputStream getInputStream()

"Returns the input stream connected to the normal output of the subprocess.
The stream obtains data piped from the standard output of the process represented by this Process object."
public abstract OutputStream getOutputStream()
"Returns the output stream connected to the normal input of the subprocess.
Output to the stream is piped into the standard input of the process represented by this Process object."

public abstract InputStream getErrorStream()

"Returns the input stream connected to the error output of the subprocess.
The stream obtains data piped from the error output of the process represented by this Process object."

到这里,我们就明白里其中的因果>从exec()返回的Process的对象中调用获取流的方法.从而达到目的!

具体做法

在需要使用exec()去执行命令并获取返回值的时候,具体的做法是什么呢?

仅仅围绕这个思路:"从exec()返回的Process的对象中调用获取流的方法getXStream"

String s = null;
              Process p = Runtime
                      .getRuntime()
                      .exec(
                              new String[]{"/bin/sh",
                                          "-c",
                                          "java HelloWorld"},null,dir);
 
              BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
              BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
 
              //打印出输出结果
              log.info("标准输出命令");
              while ((s = stdInput.readLine()) != null) {
                    log.info(s);
              }
 
              log.info("标准错误的输出命令");
              while ((s = stdError.readLine()) != null) {
                    log.info(s);
              }

其中

dir 是我的HelloWorld.class的存放目录,只能这样用,请参照这篇

HelloWorld.java 的内容是Sysotem.out.println打印几行Hello World,此处没有编译,使用时应注意.

到此,大功告成!

Runtime.exec 陷阱

该类java.lang.Runtime具有一个称为的静态方法getRuntime(),该方法检索当前的Java Runtime Environment。这是获得对该Runtime对象的引用的唯一方法。使用该参考,您可以通过调用Runtime类的exec()方法来运行外部程序。开发人员经常调用此方法来启动浏览器,以显示HTML的帮助页面。

该exec()命令有四个重载版本:

public Process exec(String command);
public Process exec(String [] cmdArray);
public Process exec(String command, String [] envp);
public Process exec(String [] cmdArray, String [] envp);

对于这些方法中的每一个,命令(可能还有一组参数)都传递给特定于操作系统的函数调用。随后,这将参考Process返回给Java VM的类来创建特定于操作系统的进程(正在运行的程序)。所述Process类是一个抽象类,因为一个特定的子类Process存在于每个操作系统。

您可以将三个可能的输入参数传递给这些方法:

  • 一个字符串,代表要执行的程序和该程序的所有参数
  • 字符串数组,用于将程序与其参数分开
  • 一组环境变量

以形式传递环境变量name=value。如果exec()对程序及其参数使用单个字符串的版本,请注意,通过StringTokenizer类使用空格作为分隔符来解析该字符串。

IllegalThreadStateException

要执行Java VM外部的进程,我们使用exec()方法。要查看外部进程返回的值,我们exitValue()。

如果外部过程尚未完成,则该exitValue()方法将抛出IllegalThreadStateException;。这就是该程序失败的原因。尽管文档中说明了这一事实,但为什么不能等到此方法可以给出有效答案呢?

对该Process类中可用的方法进行更彻底的研究,就会发现waitFor()可以做到这一点的方法。实际上,waitFor()还会返回退出值,这意味着您将不会使用exitValue()和waitFor()彼此结合,而是会选择一个或另一个。你会使用的唯一可能的时间exitValue(),而不是waitFor()会当你不希望你的程序阻止等待外部过程中可能永远不会完成。与其使用该waitFor()方法,不如将一个被调用的布尔参数waitFor传入该exitValue()方法以确定当前线程是否应等待。布尔值会更有利,因为exitValue()是此方法的更合适的名称,两个方法在不同条件下不必执行相同的功能。这种简单的条件判别是输入参数的领域。

import java.util.*;
import java.io.*;
public class BadExecJavac
{
    public static void main(String args[])
    {
        try
        {            
            Runtime rt = Runtime.getRuntime();
            Process proc = rt.exec("javac");
            int exitVal = proc.exitValue();
            System.out.println("Process exitValue: " + exitVal);
        } catch (Throwable t)
          {
            t.printStackTrace();
          }
    }
}
import java.util.*;
import java.io.*;
public class BadExecJavac2
{
    public static void main(String args[])
    {
        try
        {            
            Runtime rt = Runtime.getRuntime();
            Process proc = rt.exec("javac");
            int exitVal = proc.waitFor();
            System.out.println("Process exitValue: " + exitVal);
        } catch (Throwable t)
          {
            t.printStackTrace();
          }
    }
}

因此,为避免此陷阱,请捕获IllegalThreadStateException或等待该过程完成。

为什么Runtime.exec()挂起

由于某些本机平台仅为标准输入和输出流提供有限的缓冲区大小,因此未能及时写入子流程的输入流或读取子流程的输出流可能导致子流程阻塞,甚至死锁。

现在,让我们关注JDK文档并处理javac过程的输出。当您javac不带任何参数运行时,它会生成一组用法语句,这些用法语句描述了如何运行程序以及所有可用程序选项的含义。知道这将stderr流到流,您可以轻松地编写一个程序以在等待进程退出之前耗尽该流。清单4.3完成了该任务。尽管此方法行之有效,但这不是一个好的通用解决方案。因此,清单4.3的程序被命名为MediocreExecJavac;。它仅提供平庸的解决方案。更好的解决方案将同时清空标准错误流和标准输出流。最好的解决方案是同时清空这些流(稍后再说明)

import java.util.*;
import java.io.*;
public class MediocreExecJavac
{
    public static void main(String args[])
    {
        try
        {            
            Runtime rt = Runtime.getRuntime();
            Process proc = rt.exec("javac");
            InputStream stderr = proc.getErrorStream();
            InputStreamReader isr = new InputStreamReader(stderr);
            BufferedReader br = new BufferedReader(isr);
            String line = null;
            System.out.println("<ERROR>");
            while ( (line = br.readLine()) != null)
                System.out.println(line);
            System.out.println("</ERROR>");
            int exitVal = proc.waitFor();
            System.out.println("Process exitValue: " + exitVal);
        } catch (Throwable t)
          {
            t.printStackTrace();
          }
    }
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 解决BigDecimal转long丢失精度的问题

    解决BigDecimal转long丢失精度的问题

    这篇文章主要介绍了解决BigDecimal转long丢失精度的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • 利用Java工具类Hutool实现验证码校验功能

    利用Java工具类Hutool实现验证码校验功能

    这篇文章主要介绍了利用Java工具类Hutool实现验证码校验功能,利用Hutool实现验证码校验,校验的Servlet与今天的第一篇是一样的,唯一就是验证码的生成是不一样的,利用Hutool生成验证码更快捷.需要的朋友可以参考下
    2022-10-10
  • IDEA 程序包不存在,找不到符号但是明明存在对应的jar包(问题分析及解决方案)

    IDEA 程序包不存在,找不到符号但是明明存在对应的jar包(问题分析及解决方案)

    这篇文章主要介绍了IDEA 程序包不存在,找不到符号但是明明存在对应的jar包 的解决方案,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-08-08
  • 使用自定义注解进行restful请求参数的校验方式

    使用自定义注解进行restful请求参数的校验方式

    这篇文章主要介绍了使用自定义注解进行restful请求参数的校验方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • Maven+Tomcat8 实现自动化部署的方法

    Maven+Tomcat8 实现自动化部署的方法

    本篇文章主要介绍了Maven+Tomcat8 实现自动化部署的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-10-10
  • Java设计模式之单例模式Singleton Pattern详解

    Java设计模式之单例模式Singleton Pattern详解

    这篇文章主要介绍了Java设计模式之单例模式Singleton Pattern详解,一些常用的工具类、线程池、缓存,数据库,数据库连接池、账户登录系统、配置文件等程序中可能只允许我们创建一个对象,这就需要单例模式,需要的朋友可以参考下
    2023-12-12
  • SpringMVC之返回JSON的三种方式

    SpringMVC之返回JSON的三种方式

    现在都是前后端分离了,后端只需要跟前端提供restful接口,所有接口都返回json格式数据即可,SpringMVC通常有3种方式向前端输出json格式数据,下面我们就来了解一下
    2023-06-06
  • spring data jpa查询一个实体类的部分属性方式

    spring data jpa查询一个实体类的部分属性方式

    这篇文章主要介绍了spring data jpa查询一个实体类的部分属性方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-02-02
  • 关于两个BeanUtils.copyProperties()的用法及区别

    关于两个BeanUtils.copyProperties()的用法及区别

    这篇文章主要介绍了关于两个BeanUtils.copyProperties()的用法及区别说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • SpringBoot超详细分析启动流程

    SpringBoot超详细分析启动流程

    今天小编就为大家分享一篇关于SpringBoot整个启动过程的分析,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2022-07-07

最新评论