现在的位置: 首页 > 综合 > 正文

Runtime.exec() 的陷阱

2018年02月15日 ⁄ 综合 ⁄ 共 19870字 ⁄ 字号 评论关闭

转自:http://blog.csdn.net/vernonzheng/article/details/8221452

原文:http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html


作为Java语言的一部分。java.lang包被隐藏的导入到每一个Java程序。这个包的表面陷阱,经常影响到大多数程序员。这个月,我将讨论运行时exec()方法时的潜伏陷阱。

陷阱4:当运行exec()不会


java.lang.Runtime类,突出了静态方法calledgetRuntime(),,它会检索当前的Java运行时环境。这是唯一的方法来获取Runtime对象的引用。获取该引用,您通过可以调用Runtime类的exec()方法运行外部程序。开发人员经常调用这个方法来启动浏览器显示一个HTML帮助页面。

exec()有四个重载:

[java] view
plain
copy

  1. <span style="font-family:SimSun;">public Process exec(String command);  
  2. public Process exec(String [] cmdArray);  
  3. public Process exec(String command, String [] envp);  
  4. public Process exec(String [] cmdArray, String [] envp); </span>  


对于每个这样的方法,都会产生一个命令,并可能携带一组参数——被传递给一个特定操作系统的函数调用。这随后创建一个特定操作系统的进程(一个运行着的程序),procss类将持有该程序返回Java VM的引用。这个procss类是一个抽象类,具体子类的实现依赖于不同的底层操作系统。

你可以通过三种可能的输入参数到这些方法:

1、一个字符串,表示程序执行和程序的任何参数。

2、一个字符串数组,通过参数来区分出程序的实现功能。

3、一个环境变量的数组

传递环境变量是,使用格式化的方式:名称=值。如果你使用单个字符串和它的参数的方式调用exec()的重载,,注意字符串是通过StringTokenizer类被解析,使用空格作为分隔符。

陷入 IllegalThreadStateException

运行exec()的第一个陷阱,是theIllegalThreadStateException。 普遍上,第一次对api的尝试,都是基于一些最常用的方法。例如,执行一个java vm的外部过程,我们使用exec()方法。查看外部过程的返回值,我们使用process类的exitValue()方法。看到的值外部过程的回报,我们使用exitValue()方法在过程类。在我们的第一个示例中,我们将尝试执行Java编译器(javac
exe)。

清单 4.1 BadExecJavac.java
[java] view
plain
copy

  1. <span style="font-family:SimSun;">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. }</span>  


运行的BadExecJavac产生:

[java] view
plain
copy

  1. <span style="font-family:SimSun;">E:\classes\com\javaworld\jpitfalls\article2>java BadExecJavac  
  2. java.lang.IllegalThreadStateException: process has not exited  
  3.         at java.lang.Win32Process.exitValue(Native Method)  
  4.         at BadExecJavac.main(BadExecJavac.java:13)</span>  


如果一个外部进程尚未完成,exitValue()方法将抛出IllegalThreadStateException。这就是程序失败的原因。尽管文档声明了这个事实,为什么这个方法不能等待一个有效性的结果返回?


更彻底的看看process的可用方法,我们发现waitFor()方法,能准确地完成这一工作。事实上,waitFor()也返回退出值,这意味着你不用同时使用exitValue()和waitFor(),而是选择其中之一。唯一可能的情况,你会使用exitValue()而不是waitFor(),是当你不希望你的程序块等待一个外部的过程,而这个外部过程可能永远不会完成。取代waitFor()方法,我宁愿在exitValue()方法内部,传递一个布尔参数称为waitFor,来确定是否当前线程应该等待。一个布尔变量会更好,因为exitValue()是一个更合适的名称,,也没有必要让两个方法在在不同条件下来执行相同的功能。这种简单的通过输入参数传递,来区分条件,执行不同的功能。

    因此,为了避免这个陷阱,要么抓住theIllegalThreadStateException或等待进程完成。

    现在,让我们在清单4.1的基础,通过等待进程完成来解决这个问题。清单4.2,程序会再次execute javac.exx,然后等待外部过程来完成。

清单4.2 BadExecJavac2.java
[java] view
plain
copy

  1. <span style="font-family:SimSun;">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. }</span>  


不幸的是,一个运行的BadExecJavac2不产生任何输出。程序挂起、一直未完成。为什么javac进程一直没有完成?


为什么 Runtime.exec() 挂起


    JDK的Javadoc文档提供了这个问题的答案:

[java] view
plain
copy

  1. <span style="font-size: 14px; "><span style="font-family: SimSun; "><strong><span style="color: rgb(255, 0, 0); ">因为一些本机平台只提供有限的缓冲区大小为标准输入和输出流,未能及时写输入流或读取输出流的子流程可能会导致子流程阻止,甚至死锁。</span></strong></span></span>  

    这只是程序员不阅读文档的一个案例。隐含常听到的建议:读好手册(RTFM)?答案是部分是的。在这种情况下,阅读Javadoc将让你停在半途;它解释说,你需要处理流到你的外部过程,但是它没有告诉你怎样做。
    
    这个问题,原因明显是大量程序员的问题和误解这个API有关信息:尽管运行exec()和流程API看起来非常简单,那简单是欺骗,因为简单,或者说是明显,使用的API是容易出错。这里给API设计师的建议是,为简单的操作保留简单的API。容易产生复杂性操作和具有特定平台依赖性应该准确反映问题域。有可能某个抽象进行的太深。这个JConfig库提供了一个示例的一个更完整的API来处理文件和流程操作(请参阅下面的Resources参考资料以获得更多信息)。


    现在,让我们遵循JDK文档和处理输出的javac过程。当您运行javac不带任何参数,它产生一组使用语句,描述了如何运行这个程序及其意义的所有可用的程序的选项。了解这些信息会到stderr(标准错误)流,您可以很容易地编写一个程序,在等待进程退出前检测这个输出流。

    清单4.3完成这个任务。虽然这种方法可以运行,但这不是一个好的通用解决方案。因此,清单4.3的程序被命名为MediocreExecJavac;它只提供了一个平庸的解决方案。一个更好的解决方案将不输出或者清空标准错误流和标准输出流。最好的解决方案将清空这些流(我之后将证明)。

清单 4.3 MediocreExecJavac.java
[java] view
plain
copy

  1. <span style="font-family:SimSun;">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. }</span>  


运行MediocreExecJava产生:

[java] view
plain
copy

  1. <span style="font-family:SimSun;">E:\classes\com\javaworld\jpitfalls\article2>java MediocreExecJavac  
  2. <ERROR>  
  3. Usage: javac <options> <source files>  
  4. where <options> includes:  
  5.   -g                     Generate all debugging info  
  6.   -g:none                Generate no debugging info  
  7.   -g:{lines,vars,source} Generate only some debugging info  
  8.   -O                     Optimize; may hinder debugging or enlarge class files  
  9.   -nowarn                Generate no warnings  
  10.   -verbose               Output messages about what the compiler is doing  
  11.   -deprecation           Output source locations where deprecated APIs are used  
  12.   -classpath <path>      Specify where to find user class files  
  13.   -sourcepath <path>     Specify where to find input source files  
  14.   -bootclasspath <path>  Override location of bootstrap class files  
  15.   -extdirs <dirs>        Override location of installed extensions  
  16.   -d <directory>         Specify where to place generated class files  
  17.   -encoding <encoding>   Specify character encoding used by source files  
  18.   -target <release>      Generate class files for specific VM version  
  19. </ERROR>  
  20. Process exitValue: 2</span>  


    所以,MediocreExecJavac运行产生一个退出值2。通常,一个退出值0表示成功,任何非零值表示一个错误。这些退出值的含义取决于特定的操作系统。一个Win32错误值为2是一个“未找到文件”错误。这是有道理的,因为javac期望我们遵循的程序源代码文件进行编译。

    因此,绕过第二个陷阱——永远挂在运行时exec()——如果你运行的程序产生输出或期望的输入,确保程序的输入和输出流。

假设一个命令是一个可执行程序


    在Windows操作系统,许多新程序员运行exec(),当试图使用它来完成为非执行命令,像dir和复制,他们就掉进了运行exec()的第三陷阱。清单4.4展示的正是这种情况:

清单 4.4 BadExecWinDir.java
[java] view
plain
copy

  1. <span style="font-family:SimSun;">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. }</span>  


运行BadExecWinDir输出:


[java] view
plain
copy

  1. <span style="font-family:SimSun;">E:\classes\com\javaworld\jpitfalls\article2>java BadExecWinDir  
  2. java.io.IOException: CreateProcess: dir error=2  
  3.         at java.lang.Win32Process.create(Native Method)  
  4.         at java.lang.Win32Process.<init>(Unknown Source)  
  5.         at java.lang.Runtime.execInternal(Native Method)  
  6.         at java.lang.Runtime.exec(Unknown Source)  
  7.         at java.lang.Runtime.exec(Unknown Source)  
  8.         at java.lang.Runtime.exec(Unknown Source)  
  9.         at java.lang.Runtime.exec(Unknown Source)  
  10.         at BadExecWinDir.main(BadExecWinDir.java:12)</span>  

   如前所述,误差值为2的意思是“未找到文件”,在这种情况下,意味着可执行文件名为dir.exe不能被发现。这是因为目录命令是Windows命令解释器的一部分,而不是一个单独的可执行文件。要运行Windows命令解释器,执行orcmd.exe或者command.com,这取决于您使用的Windows操作系统。清单4.5运行的一个Windows命令解释器,然后执行用户提供的命令(如。,dir)。

清单 4.5 GoodWindowsExec.java

[java] view
plain
copy

  1. <span style="font-family:SimSun;">import java.util.*;  
  2. import java.io.*;  
  3. class StreamGobbler extends Thread  
  4. {  
  5.     InputStream is;  
  6.     String type;  
  7.       
  8.     StreamGobbler(InputStream is, String type)  
  9.     {  
  10.         this.is = is;  
  11.         this.type = type;  
  12.     }  
  13.       
  14.     public void run()  
  15.     {  
  16.         try  
  17.         {  
  18.             InputStreamReader isr = new InputStreamReader(is);  
  19.             BufferedReader br = new BufferedReader(isr);  
  20.             String line=null;  
  21.             while ( (line = br.readLine()) != null)  
  22.                 System.out.println(type + ">" + line);      
  23.             } catch (IOException ioe)  
  24.               {  
  25.                 ioe.printStackTrace();    
  26.               }  
  27.     }  
  28. }  
  29. public class GoodWindowsExec  
  30. {  
  31.     public static void main(String args[])  
  32.     {  
  33.         if (args.length < 1)  
  34.         {  
  35.             System.out.println("USAGE: java GoodWindowsExec <cmd>");  
  36.             System.exit(1);  
  37.         }  
  38.           
  39.         try  
  40.         {              
  41.             String osName = System.getProperty("os.name" );  
  42.             String[] cmd = new String[3];  
  43.             if( osName.equals( "Windows NT" ) )  
  44.             {  
  45.                 cmd[0] = "cmd.exe" ;  
  46.                 cmd[1] = "/C" ;  
  47.                 cmd[2] = args[0];  
  48.             }  
  49.             else if( osName.equals( "Windows 95" ) )  
  50.             {  
  51.                 cmd[0] = "command.com" ;  
  52.                 cmd[1] = "/C" ;  
  53.                 cmd[2] = args[0];  
  54.             }  
  55.               
  56.             Runtime rt = Runtime.getRuntime();  
  57.             System.out.println("Execing " + cmd[0] + " " + cmd[1]   
  58.                                + " " + cmd[2]);  
  59.             Process proc = rt.exec(cmd);  
  60.             // any error message?  
  61.             StreamGobbler errorGobbler = new   
  62.                 StreamGobbler(proc.getErrorStream(), "ERROR");              
  63.               
  64.             // any output?  
  65.             StreamGobbler outputGobbler = new   
  66.                 StreamGobbler(proc.getInputStream(), "OUTPUT");  
  67.                   
  68.             // kick them off  
  69.             errorGobbler.start();  
  70.             outputGobbler.start();  
  71.                                       
  72.             // any error???  
  73.             int exitVal = proc.waitFor();  
  74.             System.out.println("ExitValue: " + exitVal);          
  75.         } catch (Throwable t)  
  76.           {  
  77.             t.printStackTrace();  
  78.           }  
  79.     }  
  80. }</span>  


使用dir命令,运行GoodWindowsExec产生:

[java] view
plain
copy

  1. <span style="font-family:SimSun;">E:\classes\com\javaworld\jpitfalls\article2>java GoodWindowsExec "dir *.java"  
  2. Execing cmd.exe /C dir *.java  
  3. OUTPUT> Volume in drive E has no label.  
  4. OUTPUT> Volume Serial Number is 5C5F-0CC9  
  5. OUTPUT>  
  6. OUTPUT> Directory of E:\classes\com\javaworld\jpitfalls\article2  
  7. OUTPUT>  
  8. OUTPUT>10/23/00  09:01p                   805 BadExecBrowser.java  
  9. OUTPUT>10/22/00  09:35a                   770 BadExecBrowser1.java  
  10. OUTPUT>10/24/00  08:45p                   488 BadExecJavac.java  
  11. OUTPUT>10/24/00  08:46p                   519 BadExecJavac2.java  
  12. OUTPUT>10/24/00  09:13p                   930 BadExecWinDir.java  
  13. OUTPUT>10/22/00  09:21a                 2,282 BadURLPost.java  
  14. OUTPUT>10/22/00  09:20a                 2,273 BadURLPost1.java  
  15. ... (some output omitted for brevity)  
  16. OUTPUT>10/12/00  09:29p                   151 SuperFrame.java  
  17. OUTPUT>10/24/00  09:23p                 1,814 TestExec.java  
  18. OUTPUT>10/09/00  05:47p                23,543 TestStringReplace.java  
  19. OUTPUT>10/12/00  08:55p                   228 TopLevel.java  
  20. OUTPUT>              22 File(s)         46,661 bytes  
  21. OUTPUT>                         19,678,420,992 bytes free  
  22. ExitValue: 0</span>  


      GoodWindowsExec运行与任何相关的文档类型将启动与之关联文档类型的应用程序。例如,启动Microsoft Word来显示一个Word文档(即一个带doc扩展名的文件)。

[java] view
plain
copy

  1. >java GoodWindowsExec "yourdoc.doc"  

    注意,GoodWindowsExec使用操作系统的系统名字属性来确定,您运行的是哪种Windows操作系统,从而确定适当的命令解释器。


    在执行命令解释器,使用StreamGobbler类来处理标准错误和标准输入流。StreamGobbler通过独立线程,来清空任何传递到它的流。这个类使用一个简单的字符串类型来表示流清空,在当它将打印行输出到控制台时起作用。

    因此,为了避免运行exec()的第三个陷阱,首先不要认为一个命令是一个可执行程序;其次要了解你是否正在执行一个独立的可执行文件或一种解释命令。在结束这一节中,我将演示一个简单的命令行工具,可以帮你分析。

    值得注意的是,用于获取一个进程的输出流的方法叫做getInputStream()。要记住的是,API看待对象的角度,是从Java程序内部,而不是外部的过程。因此,外部程序的输出是Java程序的输入。这种逻辑,也表明外部程序的输入流,是一个Java程序的输出流。

Runtime.exec()不是命令行


      最后一个陷阱是,错误的假设命令行(shell)能接受的字符串,在Runtime.exec()内也能接受。运行exec()会受到更多的限制,而且不能跨平台。这个陷阱是由于用户试图使用exec()方法,接受一个符合命令行可执行的字符串。混乱可能是因为command是exec()方法的参数名。因此,程序员错误地将参数command与他可以输入命令行的东西联系起来,而不是将它看做单个程序及其参数。下面的清单4.6中,一个用户尝试执行命令和重定向输出在一个调用exec():

清单 4.6 BadWinRedirect.java

[java] view
plain
copy

  1. import java.util.*;  
  2. import java.io.*;  
  3. // StreamGobbler omitted for brevity  
  4. public class BadWinRedirect  
  5. {  
  6.     public static void main(String args[])  
  7.     {  
  8.         try  
  9.         {              
  10.             Runtime rt = Runtime.getRuntime();  
  11.             Process proc = rt.exec("java jecho 'Hello World' > test.txt");  
  12.             // any error message?  
  13.             StreamGobbler errorGobbler = new   
  14.                 StreamGobbler(proc.getErrorStream(), "ERROR");              
  15.               
  16.             // any output?  
  17.             StreamGobbler outputGobbler = new   
  18.                 StreamGobbler(proc.getInputStream(), "OUTPUT");  
  19.                   
  20.             // kick them off  
  21.             errorGobbler.start();  
  22.             outputGobbler.start();  
  23.                                       
  24.             // any error???  
  25.             int exitVal = proc.waitFor();  
  26.             System.out.println("ExitValue: " + exitVal);          
  27.         } catch (Throwable t)  
  28.           {  
  29.             t.printStackTrace();  
  30.           }  
  31.     }  
  32. }  

运行BadWinRedirect 产生:

E:\classes\com\javaworld\jpitfalls\article2>java BadWinRedirect
OUTPUT>'Hello World' > test.txt
ExitValue: 0

    该程序BadWinRedirect试图重定向Java版本的echo程序输出到文件test.txt。 然而,我们发现文件test.txt并不存在。jecho的程序只需要它的命令行参数,并将其写入到标准输出流。(你会发现jecho的源代码在源代码可供下载inResources.)清单4.6,用户认为你可以重定向标准输出到一个文件中,就像你可能在一个DOS命令行中执行一样。然而,你不能通过这种方法重定向输出。这里不正确的假设是,exec()方法会像一个shell解析器一样,但是它不会。相反,exec()执行一个单一可执行文件(一个程序或脚本)。如果你想处理流重定向或管道,要么用另一个程序实现,你必须通过编程,使用java。io包。清单4.7
完成重定向标准输出流的jecho流程到一个文件中。


清单4.7 GoodWinRedirect.java

[java] view
plain
copy

  1. import java.util.*;  
  2. import java.io.*;  
  3. class StreamGobbler extends Thread  
  4. {  
  5.     InputStream is;  
  6.     String type;  
  7.     OutputStream os;  
  8.       
  9.     StreamGobbler(InputStream is, String type)  
  10.     {  
  11.         this(is, type, null);  
  12.     }  
  13.     StreamGobbler(InputStream is, String type, OutputStream redirect)  
  14.     {  
  15.         this.is = is;  
  16.         this.type = type;  
  17.         this.os = redirect;  
  18.     }  
  19.       
  20.     public void run()  
  21.     {  
  22.         try  
  23.         {  
  24.             PrintWriter pw = null;  
  25.             if (os != null)  
  26.                 pw = new PrintWriter(os);  
  27.                   
  28.             InputStreamReader isr = new InputStreamReader(is);  
  29.             BufferedReader br = new BufferedReader(isr);  
  30.             String line=null;  
  31.             while ( (line = br.readLine()) != null)  
  32.             {  
  33.                 if (pw != null)  
  34.                     pw.println(line);  
  35.                 System.out.println(type + ">" + line);      
  36.             }  
  37.             if (pw != null)  
  38.                 pw.flush();  
  39.         } catch (IOException ioe)  
  40.             {  
  41.             ioe.printStackTrace();    
  42.             }  
  43.     }  
  44. }  
  45. public class GoodWinRedirect  
  46. {  
  47.     public static void main(String args[])  
  48.     {  
  49.         if (args.length < 1)  
  50.         {  
  51.             System.out.println("USAGE java GoodWinRedirect <outputfile>");  
  52.             System.exit(1);  
  53.         }  
  54.           
  55.         try  
  56.         {              
  57.             FileOutputStream fos = new FileOutputStream(args[0]);  
  58.             Runtime rt = Runtime.getRuntime();  
  59.             Process proc = rt.exec("java jecho 'Hello World'");  
  60.             // any error message?  
  61.             StreamGobbler errorGobbler = new   
  62.                 StreamGobbler(proc.getErrorStream(), "ERROR");              
  63.               
  64.             // any output?  
  65.             StreamGobbler outputGobbler = new   
  66.                 StreamGobbler(proc.getInputStream(), "OUTPUT", fos);  
  67.                   
  68.             // kick them off  
  69.             errorGobbler.start();  
  70.             outputGobbler.start();  
  71.                                       
  72.             // any error???  
  73.             int exitVal = proc.waitFor();  
  74.             System.out.println("ExitValue: " + exitVal);  
  75.             fos.flush();  
  76.             fos.close();          
  77.         } catch (Throwable t)  
  78.           {  
  79.             t.printStackTrace();  
  80.           }  
  81.     }  
  82. }  

运行 GoodWinRedirect 产生:

[java] view
plain
copy

  1. E:\classes\com\javaworld\jpitfalls\article2>java GoodWinRedirect test.txt  
  2. OUTPUT>'Hello World'  
  3. ExitValue: 0  

    在运行GoodWinRedirect、test.txt确实存在。解决这个陷阱很简单,就是通过外部进程控制重定向的标准输出流,独立于exec()方法。我们创建一个单独的OutputStream、读取的文件名,打开文件,我们将从派生进程的标准输出到文件。清单4.7通过对StreamGobbler类添加一个新的构造器来完成这个任务。新构造函数有三个参数:读入输入流,String类型标记我们是否正在使用读入输出流,重定向输入。这个新版本的StreamGobbler并不违反任何代码之前使用的,因为我们没有改变现有的公共API——我们只扩展它。
    因为参数来运行exec()是依赖于操作系统、不同系统之间的适用命令会有所变化。所以,在最终确定Runtime.exec()参数和编写代码,快速测试。清单4.8是一个简单的命令行工具,允许你这样做。
    这是一个有用的练习:尝试修改TestExec重定向标准输入和标准输出到一个文件。当在Windows 95或Windows 98上执行javac编译器,这将解决错误消息超出命令行有限缓存的问题


清单4.8 TestExec.java

  1. import java.util.*;  
  2. import java.io.*;  
  3. // class StreamGobbler omitted for brevity  
  4. public class TestExec  
  5. {  
  6.     public static void main(String args[])  
  7.     {  
  8.         if (args.length < 1)  
  9.         {  
  10.             System.out.println("USAGE: java TestExec \"cmd\"");  
  11.             System.exit(1);  
  12.         }  
  13.           
  14.         try  
  15.         {  
  16.             String cmd = args[0];  
  17.             Runtime rt = Runtime.getRuntime();  
  18.             Process proc = rt.exec(cmd);  
  19.               
  20.             // any error message?  
  21.             StreamGobbler errorGobbler = new   
  22.                 StreamGobbler(proc.getErrorStream(), "ERR");              
  23.               
  24.             // any output?  
  25.             StreamGobbler outputGobbler = new   
  26.                 StreamGobbler(proc.getInputStream(), "OUT");  
  27.                   
  28.             // kick them off  
  29.             errorGobbler.start();  
  30.             outputGobbler.start();  
  31.                                       
  32.             // any error???  
  33.             int exitVal = proc.waitFor();  
  34.             System.out.println("ExitValue: " + exitVal);  
  35.         } catch (Throwable t)  
  36.           {  
  37.             t.printStackTrace();  
  38.           }  
  39.     }  
  40. }  

运行TestExec 启动Netscape浏览器和加载Java帮助文档  产生:

[java] view
plain
copy

  1. E:\classes\com\javaworld\jpitfalls\article2>java TestExec "e:\java\docs\index.html"  
  2. java.io.IOException: CreateProcess: e:\java\docs\index.html error=193  
  3.         at java.lang.Win32Process.create(Native Method)  
  4.         at java.lang.Win32Process.<init>(Unknown Source)  
  5.         at java.lang.Runtime.execInternal(Native Method)  
  6.         at java.lang.Runtime.exec(Unknown Source)  
  7.         at java.lang.Runtime.exec(Unknown Source)  
  8.         at java.lang.Runtime.exec(Unknown Source)  
  9.         at java.lang.Runtime.exec(Unknown Source)  
  10.         at TestExec.main(TestExec.java:45)  

    我们的第一个测试失败,错误193。Win32误差值193“不是一个有效的Win32应用程序。“这个错误告诉我们,没有找到关联的应用程序(如网景浏览器)存在,而且这一流程不能运行一个HTML文件没有关联的应用程序。
因此,我们尝试测试,这次又给它一个完整路径网景。(或者,我们可以添加到我们的PATH  environment网景变量)。第二次运行的TestExec产生:


  1. E:\classes\com\javaworld\jpitfalls\article2>java TestExec   
  2. "e:\program files\netscape\program\netscape.exe e:\java\docs\index.html"  
  3. ExitValue: 0  

ok!网景浏览器的打开,然后装入Java帮助文档。
一个额外的改进包括一个命令行开关,使TestExec接受从标准输入的输入流。然后您将使用Process.getOutputStream()方法通过输入派生外部程序。


总之,遵循这些法则,以避免的陷阱在运行时执行():

你不能从外部过程获得一个退出状态,直到它已经退出
你必须从你外部程序立即处理输入、输出和错误流
您必须使用运行时exec()来执行程序
你不能使用运行时执行()就像一个命令行

抱歉!评论已关闭.