※※Java调用Runtime.exec()要注意的问题
标签:execJavaRuntime
字体:【默认中大】
http://it.superkoo.com/#/topic/479/

最近开发一个项目需要在JAVA中调用VC写的一个EXE程序,首先想到的方法肯定是用Runtime.exec(),但一写就发现,事情并没有那么的简单。后来经过多番调试与查找资料才明白了其中的细节:

(1)等待命令执行结束用waitFor(),其返回值就是命令的返回值
(2)如果出现程序执行被挂起,没有任何反应的情况,是由于没有读取命令子进程的正常输出流或错误输出流导致缓冲区被占满,进程被锁住。这个时候需要把输出流中的内容给读出来。最好的做法是使用两个线程,分别同时读取正常输出流和错误输出流。
(3)执行Windows平台上的命令时使用cmd.exe /C,如cmd.exe /C dir。
(4)记得关闭命令子进程的输入流,通过Process.getOutputStream().close(),这样不会导致命令子进程被锁住。

1、Runtime.exec的用法

The class java.lang.Runtime features a static method called getRuntime(), which retrieves the current Java Runtime Environment. This is the only way to obtain a reference to the Runtime Object. With this reference you can run external programs by invoking the Runtime class’s exec() method. Developers often call this method to launch a browser for displaying a help page in HTML.

There are four overloaded version of the exec() command:

Public Process exec(String command);

Public Process exec(String[] cmdArray);

Public Process exec(String command, String []envp);

Public Process exec(String [] cmdArray, String []envp);

For each of these methods, a command—and possible and possibly a set of arguments—is passed to an operating system function call. This subsequently creates an operating system specific process(a running program )with a reference to a Process class returned to the Java VM. The Process is a abstract class ,because a specific subclass of Process exists for each operating system.

You can pass three possible input params into to these methods:

1.a single string that represents both the program to execute and any args to the program.

2.an array of strings that separate the program from its argumen

3.an array of the environment variables.

Pass the environment variables in the form name=value. If you use the version of exec() with a sigle string for both the program and its arguments, note that the string is parsed using white space as the delimiter via the StringTokenizer class.

The first pitfall relating to Runtime.exec() is the IllegalThreadStateException the prevalent first test of an API is to code its most obvious methods.To see the value the external process returns we use the exitValue() method on the Process class. In our first example, we will attempt to execute the Java compliere(javac.exe)

2、一个错误的程序示例

java 代码
  1. import java.util.*;
  2. import java.io.*;
  3. public class BadExecJavac
  4. {
  5.     public static void main(String args[])
  6.     {
  7.         try
  8.         {           
  9.             Runtime rt = Runtime.getRuntime();
  10.             Process proc = rt.exec("javac");
  11.             int exitVal = proc.exitValue();
  12.             System.out.println("Process exitValue: " + exitVal);
  13.         } catch (Throwable t)
  14.           {
  15.             t.printStackTrace();
  16.           }
  17.     }
  18. }

运行结果如下:

E:\classes\com\javaworld\jpitfalls\article2>java BadExecJavac

java.lang.IllegalThreadStateException: process has not exited

         at java.lang.Win32Process.exitValue(Native Method)

         at BadExecJavac.main(BadExecJavac.java:13)

这是因为当进程还没有结束的情况下,调用exitValue方法会抛出IllegalThreadStateException.当然了我们会问为什吗这个方法不会等到进程结束在返回一个合理的值?

在检查Process类的所有可用方法以后我们发现WairFor()是一个更合适的方法。事实上waitFor也会返回exit value。这意味着你不可以同时用exitvalue和waitfor,而是只能选择一个。

当然了也有情况你要在waitfor之前用exitvalue方法:就是你不想因为外部程序永远无法完成而一直等待下去。

因此为了避免这个陷阱,我们要么捕获IllegalThreadStateException异常,要么等待进程完成。我们相当然的以为可以用waitfor来等待程序的结束。代码如下:

java 代码
  1. import java.util.*;
  2. import java.io.*;
  3. public class BadExecJavac2
  4. {
  5.      public static void main(String args[])
  6.      {
  7.          try
  8.          {             
  9.             Runtime rt = Runtime.getRuntime();
  10.              Process proc = rt.exec("javac");
  11.              int exitVal = proc.waitFor();
  12.              System.out.println("Process exitValue: " + exitVal);
  13.          } catch (Throwable t)
  14.            {
  15.              t.printStackTrace();
  16.            }
  17.      }
  18. }

这次在linux下面返回的结果是2,而在windows下面据说程序会挂起,关于其原因我们可以在jdk文档中找到部分解释:因为一些操作系统为标准的输入输出仅仅提供有限的缓冲区,当不能正确的将信息写进输入流或者从输出流中获取信息时,就会导致子进程的阻塞,甚至死锁。

3、一个平庸的解决方案

现在我们就根据jdk文档来处理javac进程的输出,当你不带任何参数运行javac时,它会打印出一系列的有用的提示信息。而这些会被传送到stderr流中。我们可以写程序在其返回前获取这些信息。下面的代码提供了一个平庸的解决方案。

java 代码
  1. import java.util.*;
  2. import java.io.*;
  3. public class MediocreExecJavac
  4. {
  5.      public static void main(String args[])
  6.      {
  7.          try
  8.          {             
  9.             Runtime rt = Runtime.getRuntime();
  10.              Process proc = rt.exec("javac");
  11.              InputStream stderr = proc.getErrorStream();
  12.              InputStreamReader isr = new InputStreamReader(stderr);
  13.              BufferedReader br = new BufferedReader(isr);
  14.              String line = null;
  15.              System.out.println("<ERROR>");
  16.              while ( (line = br.readLine()) != null)
  17.                  System.out.println(line);
  18.              System.out.println("</ERROR>");
  19.              int exitVal = proc.waitFor();
  20.              System.out.println("Process exitValue: " + exitVal);
  21.          } catch (Throwable t)
  22.            {
  23.              t.printStackTrace();
  24.            }
  25.      }
  26. }

这次程序可以正确的输出了提示信息,但是我们应该注意到其返回代码是2,我们知道任何非0的返回代码都表示程序不正常。所以我们需要进一步的查找原因。对于win32而言是file not found,很明显javac期望我们提供编译的文件。所以对于永远挂起的问题,如果你运行的程序会有输出或者要求输出入时,你需要处理输出和输入。

3、不要把命令当成一个可以执行的程序

很多新手把一些windows的命令当中可以独立运行的程序因此他们就陷入了Runtime的另一个陷阱,如下面的例子所示:

java 代码
  1. import java.util.*;
  2. import java.io.*;
  3. public class BadExecWinDir
  4. {
  5.      public static void main(String args[])
  6.      {
  7.          try
  8.          {             
  9.             Runtime rt = Runtime.getRuntime();
  10.              Process proc = rt.exec("dir");
  11.              InputStream stdin = proc.getInputStream();
  12.              InputStreamReader isr = new InputStreamReader(stdin);
  13.              BufferedReader br = new BufferedReader(isr);
  14.              String line = null;
  15.              System.out.println("<OUTPUT>");
  16.              while ( (line = br.readLine()) != null)
  17.                  System.out.println(line);
  18.              System.out.println("</OUTPUT>");
  19.              int exitVal = proc.waitFor();             
  20.             System.out.println("Process exitValue: " + exitVal);
  21.          } catch (Throwable t)
  22.            {
  23.              t.printStackTrace();
  24.            }
  25.      }
  26. }

据说在windows下面会抛出如下的异常:

java.io.IOException: CreateProcess: dir error=2

         at java.lang.Win32Process.create(Native Method)

         at java.lang.Win32Process.<init>(Unknown Source)

         at java.lang.Runtime.execInternal(Native Method)

         at java.lang.Runtime.exec(Unknown Source)

         at java.lang.Runtime.exec(Unknown Source)

         at java.lang.Runtime.exec(Unknown Source)

         at java.lang.Runtime.exec(Unknown Source)

         at BadExecWinDir.main(BadExecWinDir.java:12)

我在linux下面运行的结果是正确的。前面说了在win32下面2代表是文件没有找到,而在这种情况下表明是dir.exe没有找到,(因为根本就没有这个文件,他们都被封装到common.com (win95)或者cmd.exe中了。

下面我们列出一个正确的处理Process的输入输出流的方法。需要用一个线程类。

java 代码
  1. import java.util.*;
  2. import java.io.*;
  3. class StreamGobbler extends Thread
  4. {
  5.      InputStream is;
  6.      String type;
  7.     StreamGobbler(InputStream is, String type)
  8.      {
  9.          this.is = is;
  10.          this.type = type;
  11.      }
  12.      
  13.     public void run()
  14.      {
  15.          try
  16.          {
  17.              InputStreamReader isr = new InputStreamReader(is);
  18.              BufferedReader br = new BufferedReader(isr);
  19.              String line=null;
  20.              while ( (line = br.readLine()) != null)
  21.                  System.out.println(type + ">" + line);     
  22.             } catch (IOException ioe)
  23.                {
  24.                  ioe.printStackTrace();  
  25.               }
  26.      }
  27. }

用于专门的处理输入输出。

java 代码
  1. public class GoodWindowsExec
  2. {
  3.      public static void main(String args[])
  4.      {
  5.          if (args.length < 1)
  6.          {
  7.              System.out.println("USAGE: java GoodWindowsExec <cmd>");
  8.              System.exit(1);
  9.          }
  10.          
  11.         try
  12.          {             
  13.             String osName = System.getProperty("os.name" );
  14.              String[] cmd = new String[3];
  15.              if( osName.equals( "Windows NT" ) )
  16.              {
  17.                  cmd[0] = "cmd.exe" ;
  18.                  cmd[1] = "/C" ;
  19.                  cmd[2] = args[0];
  20.              }
  21.              else if( osName.equals( "Windows 95" ) )
  22.              {
  23.                  cmd[0] = "command.com" ;
  24.                  cmd[1] = "/C" ;
  25.                  cmd[2] = args[0];
  26.              }
  27.              
  28.             Runtime rt = Runtime.getRuntime();
  29.              System.out.println("Execing " + cmd[0] + " " + cmd[1] 
  30.                                + " " + cmd[2]);
  31.              Process proc = rt.exec(cmd);
  32.              // any error message?
  33.              StreamGobbler errorGobbler = new 
  34.                 StreamGobbler(proc.getErrorStream(), "ERROR");             
  35.             
  36.             // any output?
  37.              StreamGobbler outputGobbler = new 
  38.                 StreamGobbler(proc.getInputStream(), "OUTPUT");
  39.                  
  40.             // kick them off
  41.              errorGobbler.start();
  42.              outputGobbler.start();
  43.                                      
  44.             // any error???
  45.              int exitVal = proc.waitFor();
  46.              System.out.println("ExitValue: " + exitVal);         
  47.         } catch (Throwable t)
  48.            {
  49.              t.printStackTrace();
  50.            }
  51.      }
  52. }

如果运行如下命令上面的代码会调用word程序

>java GoodWindowExec “abc.doc”

也就是说文件类型如果window能够识别它就会调用对应的程序处理。

StreamGlobbler的最重要作用是他会清空所有的传递给他的inputstream,这样不会造成Process阻塞或者死锁。

另外一种实现方式:

java 代码
  1. package cmd; 
  2.  
  3. import java.io.BufferedReader; 
  4. import java.io.InputStream; 
  5. import java.io.InputStreamReader; 
  6.  
  7. class StreamDrainer implements Runnable { 
  8.     private InputStream ins; 
  9.  
  10.     public StreamDrainer(InputStream ins) { 
  11.         this.ins = ins; 
  12.     } 
  13.  
  14.     public void run() { 
  15.         try { 
  16.             BufferedReader reader = new BufferedReader( 
  17.                     new InputStreamReader(ins)); 
  18.             String line = null; 
  19.             while ((line = reader.readLine()) != null) { 
  20.                 System.out.println(line); 
  21.             } 
  22.         } catch (Exception e) { 
  23.             e.printStackTrace(); 
  24.         } 
  25.     } 
  26.  
  27.  
  28. public class TestRunCmd { 
  29.  
  30.     public static void main(String[] args) { 
  31.         String[] cmd = new String[] { "cmd.exe", "/C", "wmic process get name" }; 
  32.         try { 
  33.             Process process = Runtime.getRuntime().exec(cmd); 
  34.              
  35.             new Thread(new StreamDrainer(process.getInputStream())).start(); 
  36.             new Thread(new StreamDrainer(process.getErrorStream())).start(); 
  37.              
  38.             process.getOutputStream().close(); 
  39.  
  40.             int exitValue = process.waitFor(); 
  41.             System.out.println("返回值:" + exitValue); 
  42.         } catch (Exception e) { 
  43.             e.printStackTrace(); 
  44.         } 
  45.  
  46.     } 
  47.  

4、不要把exec当成命令行,它不会重定向文件

如你在linux下面type ls> abc.txt或者在window下面dir>abc.txt 会把输出的结果通过管道重定向到文件中。但是exec不会。你必须自己写程序来处理。


※※Java调用Runtime.exec()要注意的问题相关推荐

  1. [转]Java中Runtime.exec的一些事

    0 预备知识 1 不正确的调用exitValue 2不正确的调用waitFor 3 一种可接受的调用方式 4 调用认为是可执行程序的时候容易发生的错误 5 window执行的良好示例 6 不良好的重定 ...

  2. java exec 路径_[Java] 关于java.lang.Runtime.exec()方法运行命令所在目录的探讨。 | 学步园...

    测试代码: import java.util.*; import java.io.*; publicclassBadExecJavac { publicstaticvoidmain(String ar ...

  3. 解决java使用Runtime.exec执行linux复杂命令不成功问题

    解决java使用Runtime.exec执行linux复杂命令不成功问题 参考文章: (1)解决java使用Runtime.exec执行linux复杂命令不成功问题 (2)https://www.cn ...

  4. Java Runtime.exec()的使用

    Sun的doc里其实说明还有其他的用法: exec(String[] cmdarray, String[] envp, File dir)Executes the specified command ...

  5. java exec dir的例子_java.lang.Runtime.exec(String[] cmdarray, String[] envp, File dir)方法实例...

    全屏 java.lang.Runtime.exec(String[] cmdarray, String[] envp, File dir)方法执行在指定环境和工作目录的独立进程中指定的命令和参数.字符 ...

  6. java无阻塞执行脚本,JAVA调用Shell脚本-及阻塞的解决方法

    JAVA调用Shell脚本--及阻塞的解决办法 用java调用shell,使用 Process p=Runtime.getRuntime().exec(String[] cmd); Runtime.e ...

  7. java调用python项目实战_Java调用Python

    今天遇到Java调用一个Python脚本的问题,纠结了大半天,遇到各种问题.网上搜索的大部分都是用jython,但是我想要调用的python脚本里有import urllib,这个urllib也不是什 ...

  8. java调用python库pyd_Java调用Python的两种方式

    1.前言 在与第三方程序或语言进行交互时,需要Java调用 2.使用Runtime的exec函数 在使用时需注意img = sys.argv[1]取下标为1的参数 package com; impor ...

  9. [转载] Java中Runtime的使用

    参考链接: Java中的JVM的关闭挂钩 1            JDK中Runtime的定义 http://blog.csdn.net/lysnow_oss/archive/2007/05/12/ ...

最新文章

  1. linux挂在win共享文件
  2. Meta 发布 Bean Machine 帮助衡量 AI 模型的不确定性
  3. SQL Server2008附加数据库之后显示为只读时解决方法
  4. Rest 微服务工程搭建01——微服务提供者Module模块
  5. 物资申请php,php学生捐赠物品管理系统
  6. python输入字符串str_python字符串(str)
  7. java 加密_Java版SMS4加密解密算法
  8. 微信小程序|area组件使用的地址数据文件
  9. LINUX下FORK的运行机制详细解析
  10. sql 查询数据长度
  11. 不简单的工厂:实际体验 .NET Core 2.1 新生物 HttpClientFactory
  12. arduino下载库出错_关于程序下载到最后卡住了以及自带库不能用的问题!!
  13. 基于时空图卷积网络预测交通流
  14. 单片机学习(三)中断
  15. 数据结构(一)、二叉树(BT),二叉查找树(BST),平衡二叉树(AVL树)
  16. 京东静态网页设计案例(1)
  17. Oracle数据库自动生成数据字典
  18. 前端如何实现一键截图功能?
  19. Jmeter测试最大在线用户数
  20. Redis(上)基础及8种数据类型

热门文章

  1. ambari mysql error code: 1665_ambari安装中常见的问题
  2. mysql binlog线程恢复_使用MySQL SQL线程回放Binlog实现恢复
  3. [渝粤教育] 莆田学院 电机与拖动基础(一) 参考 资料
  4. [渝粤教育] 中国传媒大学 政治传播学 参考 资料
  5. 【渝粤教育】国家开放大学2018年春季 8634-21TAndroid智能手机编程 参考试题
  6. 【渝粤题库】国家开放大学2021春1374班级管理题目
  7. 无线数传模块SI4463、SI4438、SI4432方案无线通信比对
  8. LoRa、Sigfox和NB-IoT在物联网趋势中谁是你的最佳拍档?
  9. mysql 热块_MySQL分布式集群之MyCAT(三)rule的分析【转】
  10. 请简述gouraud光照模型_《计算机图形学》试卷及答案