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

目录
  • 什么是java.lang.Runtime
  • 什么是java.lang.Process
  • 具体做法
  • Runtime.exec 陷阱
    • IllegalThreadStateException
    • 为什么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();
          }
    }
}

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

(0)

相关推荐

  • Java 常见异常(Runtime Exception )详细介绍并总结

    本文重在Java中异常机制的一些概念.写本文的目的在于方便我很长时间后若是忘了这些东西可以通过这篇文章迅速回忆起来. 1. 异常机制 1.1 异常机制是指当程序出现错误后,程序如何处理.具体来说,异常机制提供了程序退出的安全通道.当出现错误后,程序执行的流程发生改变,程序的控制权转移到异常处理器. 1.2 传统的处理异常的办法是,函数返回一个特殊的结果来表示出现异常(通常这个特殊结果是大家约定俗称的),调用该函数的程序负责检查并分析函数返回的结果.这样做有如下的弊端:例如函数返回-1代表出现异常

  • Java Runtime用法实战案例

    本文实例讲述了Java Runtime用法.分享给大家供大家参考,具体如下: 一 代码 public class GetRuntimeInfo { public static void main(String args[]) { @SuppressWarnings("unused") Runtime run = Runtime.getRuntime(); // 单例设计 String str = ""; //定义一个字符串 for (int x = 0; x <

  • Java基于Runtime调用外部程序出现阻塞的解决方法

    本文实例讲述了Java基于Runtime调用外部程序出现阻塞的解决方法, 是一个很实用的技巧.分享给大家供大家参考.具体分析如下: 有时候在java代码中会调用一些外部程序,比如SwfTools来转换swf.ffmpeg来转换视频等.如果你的代码这样写:Runtime.getRuntime().exec(command),会发现程序一下就执行完毕,而在命令行里要执行一会,是因为java没有等待外部程序的执行完毕,此时就需要使用阻塞,来等待外部程序执行结果: InputStream stderr

  • java.lang.Runtime.exec() Payload知识点详解

    有时,通过Runtime.getRuntime().exec()执行命令的有效负载有时会失败.使用Web Shell,反序列化利用或通过其他媒介时,可能会发生这种情况. 有时这是因为重定向和管道字符的使用在启动过程的上下文中没有意义.例如,在shell中执行ls> dir_listing会将当前目录的列表输出到名为dir_listing的文件中.但是在exec()函数的上下文中,该命令将被解释为获取>和dir_listing目录的列表. 有时,StringTokenizer类会破坏其中包含空格

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

    目录 什么是java.lang.Runtime 什么是java.lang.Process 具体做法 Runtime.exec 陷阱 IllegalThreadStateException 为什么Runtime.exec()挂起 在java.lang.Runtime.exec的使用中,我们经常会用到将重定向命令执行的输入/结果或者将错误信息读取出来. 那么,在使用过程中,我们如何正确的使用呢? 什么是java.lang.Runtime 首先我们要明确一点,什么是Java.lang.Runtime?

  • 调用java.lang.Runtime.exec的正确姿势分享

    目录 调用java.lang.Runtime.exec的正确姿势 两种方法 小结一下 Java Runtime.exec()注意事项 1.Runtime.exec() 有四种调用方法 2.得到程序执行返回值, 0为success 3.得到程序执行的结果或错误信息 4.Runtime.exec() 调用java.lang.Runtime.exec的正确姿势 今天写一个用到编译的程序,遇到了问题. 在调用 runtime.exec("javac HelloWorld.java"); 运行完

  • Java Spring框架创建项目与Bean的存储与读取详解

    目录 1.Spring项目的创建 1.1创建Maven项目 1.2添加spring依赖 1.3创建启动类 1.4配置国内源 2.储存或读取Bean对象 2.1添加spring配置文件 2.2创建Bean对象 2.3读取Bean对象 本文思维导图: 1.Spring项目的创建 1.1创建Maven项目 第一步,创建Maven项目,Spring也是基于Maven的. 1.2添加spring依赖 第二步,在Maven项目中添加Spring的支持(spring-context, spring-beans

  • Java和C#输入输出流的方法(详解)

    1,Java中操作方法: import java.io.*; public class FileInputStreamTest { public static void main(String[] args) throws IOException { //创建字节输入流 FileInputStream fis = new FileInputStream("FileInputStreamTest.java"); //创建一个长度为1024的竹筒 byte[] bbuf = new byt

  • Java文件操作之IO流 File类的使用详解

    File类概述 File类能新建.删除.重命名文件和目录,但不能访问文件内容本身,如果需要访问文件内容本身,则需要使用后续的输入/输出流. 要在Java程序中表示一个真实存在的文件或目录,那么必须有一个File对象,但是Java程序中的一个File对象,可能没有一个真实存在的文件或目录. File对象可以作为参数传递给流的构造器. 常用构造器 ①public File(String pathname) 以pathname为路径创建File对象,可以是绝对路径或者相对路径,如果是相对路径,则默认相

  • MyBatis流式查询的使用详解

    目录 1.应用场景说明 2.模拟excel导出场景 1.创建海量数据的sql脚本 2.MyBatis流式查询 3.Excel通用导出工具类 1.Excel导入导出工具类 2.Excel数据读取监听器 3.Excel读取数据完成回调接口 4.拆分List集合工具类 4.测试结果 5.遗留问题待处理 1.应用场景说明 MyBatis preview: JDBC三种读取方式: 1.一次全部(默认):一次获取全部. 2.流式:多次获取,一次一行. 3.游标:多次获取,一次多行. 在开发中我们经常需要会遇

  • java 中模拟UDP传输的发送端和接收端实例详解

    java 中模拟UDP传输的发送端和接收端实例详解 一.创建UDP传输的发送端 1.建立UDP的Socket服务: 2.将要发送的数据封装到数据包中: 3.通过UDP的Socket服务将数据包发送出去: 4.关闭Socket服务. import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; public class

  • C语言数据输入与输出实例详解

    C语言数据输入与输出实例详解 1 概论 C语言提供了跨平台的数据输入输出函数scanf()和printf()函数,它们可以按照指定的格式来解析常见的数据类型,例如整数,浮点数,字符和字符串等等.数据输入的来源可以是文件,控制台以及网络,而输出的终端可以是控制台,文件甚至是网页. 2 数据输出 从第一个c语言程序中,就使用了跨平台的库函数printf实现将一段文字输出到控制台,而实际上,printf()不仅可以将数据按照指定的格式输出到控制台,还可以是网页或者是指定的文件中,printf()函数执

  • Java中的数组复制(clone与arraycopy)代码详解

    JAVA数组的复制是引用传递,而并不是其他语言的值传递. 1.clone protectedObjectclone() throwsCloneNotSupportedException创建并返回此对象的一个副本."副本"的准确含义可能依赖于对象的类.这样做的目的是,对于任何对象x,表达式: x.clone()!=x为true,表达式: x.clone().getClass()==x.getClass()也为true,但这些并非必须要满足的要求.一般情况下: x.clone().equa

随机推荐