Java调用shell参考文章

重要参考文章(强力推荐,使用Process类调用外部程序必看的文章):
http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html?page=4
这篇文章讲的大概是Runtime.getRuntime()调用外部程序可能潜在的问题并给出如何解决的方法,逐步推进,是一篇不错的文章。
我的使用场景是在java中调用/bin/sh执行一些命令,并获取命令的执行结果。
Process p=Runtime.getRuntime().exec(String[] cmdArr);
因为我想要执行一些文本处理,涉及到多个程序,不可避免的需要管道操作,所以选择了上面的形式让/bin/sh代理执行管道功能。
不妨把p看成一个Java创建的与执行命令的外部进程(由/bin/sh创建)进行交互的进程:

                                            |----------------|                                        |-------------
p.getInputStream()-------|   Java创建的           |<-----Normal Output---|  /bin/sh创建的 |
                                            |    交互进程P            |                                       |   外部进程         |
p.getErrorStream()------ |                                |<------Error Output---|                          |
                                           |----------------|                                        |-------------|

从上图可以看到p的inputStream其实就是外部进程的标准输出,p的errorStream对应外部进程的标准错误。
引文建议使用两个同时处理进程P的inputStream和errorStream,这是必要的。
我第一次使用时,并没有同时处理,而是先处理错误输出,如果错误输出不为空,就直接结束。
否则再处理标准输出。
------这种做法会出现阻塞的现象---当进程P的输出很大时---不知道是怎么回事,据引文的说法,似乎是
不采用同时读时,你无法判断哪个流的数据最先开始,所以可能出现无法读取,一直阻塞的现象。
然而,当你采用同时读取的方法时,也有一些要注意的,而这在引文中并没有提到:
以下是引文给出的做法:

Listing 4.7 GoodWinRedirect.java

import java.util.*;
import java.io.*;
class StreamGobbler extends Thread
{
    InputStream is;
    String type;
    OutputStream os;
   
    StreamGobbler(InputStream is, String type)
    {
        this(is, type, null);
    }
    StreamGobbler(InputStream is, String type, OutputStream redirect)
    {
        this.is = is;
        this.type = type;
        this.os = redirect;
    }
   
    public void run()
    {
        try
        {
            PrintWriter pw = null;
            if (os != null)
                pw = new PrintWriter(os);
               
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            String line=null;
            while ( (line = br.readLine()) != null)
            {
                if (pw != null)
                    pw.println(line);
                System.out.println(type + ">" + line);   
            }
            if (pw != null)
                pw.flush();
        } catch (IOException ioe)
            {
            ioe.printStackTrace(); 
            }
    }
}


Listing 4.8 TestExec.java

import java.util.*;
import java.io.*;
// class StreamGobbler omitted for brevity
public class TestExec
{
    public static void main(String args[])
    {
        if (args.length < 1)
        {
            System.out.println("USAGE: java TestExec \"cmd\"");
            System.exit(1);
        }
       
        try
        {
            String cmd = args[0];
            Runtime rt = Runtime.getRuntime();
            Process proc = rt.exec(cmd);
           
            // any error message?
            StreamGobbler errorGobbler = new
                StreamGobbler(proc.getErrorStream(), "ERR");           
           
            // any output?
            StreamGobbler outputGobbler = new
                StreamGobbler(proc.getInputStream(), "OUT");
               
            // kick them off
            errorGobbler.start();
            outputGobbler.start();
                                   
            // any error???
            int exitVal = proc.waitFor();
            System.out.println("ExitValue: " + exitVal);

           //这里有些问题
        } catch (Throwable t)
          {
            t.printStackTrace();
          }
    }
}
使用这种方法,有时候会出现异常----提示流已经被关闭了,
   BufferedReader br = new BufferedReader(isr);
            String line=null;
            //流被关闭,无法读取,这里会抛出异常
            while ( (line = br.readLine()) != null)
            {
                if (pw != null)
                    pw.println(line);
                System.out.println(type + ">" + line);   
            }
为什么呢?
我们可以想到,因为进程P相当于给我们创建了两个管道,但是对于管道的读取,作读取的线程是不知道流什么时候关闭的,当进程P关闭管道后,线程仍然在读取,所以会抛出异常。
合理的方法是主线程等待两个读取线程的结束。
即是在
// kick them off
            errorGobbler.start();
            outputGobbler.start();
                                   
            // any error???
            int exitVal = proc.waitFor();
            System.out.println("ExitValue: " + exitVal);
           //加入
           errorGobbler.join();
          outputGobbler.join();

int exitVal = proc.waitFor();//这句只是等待进程P的结束,而此时两个线程仍然在读取的,如果就此跳过,可能会抛出异常

猜你喜欢

转载自hjw2java.iteye.com/blog/1131574