使用Java运行外部命令时出现错误流阻塞

时间:2014-04-23 15:25:59

标签: java stream runtime.exec

使用SEAndroid,我从我的Java应用程序调用Setools命令。 它与小型SEAndroid政策完美配合,现在我需要用真实的方式测试我的工具 SEAndroid政策。但不幸的是,我遇到了错误流的问题。

这里我用来调用外部命令的代码:

public static BufferedReader runCommand(final String[] args)
            throws IOException {

    BufferedReader stdInput = null;
    BufferedReader stdError = null;
    try {
        Process p = Runtime.getRuntime().exec(args);

        stdInput = new BufferedReader(new
                InputStreamReader(p.getInputStream()));

        stdError = new BufferedReader(new
                InputStreamReader(p.getErrorStream()));

        // read any errors from the attempted command
        String s = null;
        StringBuilder err = new StringBuilder();

        while ((s = stdError.readLine()) != null) {
            err.append(s + "\n");
        }
        if (err.length() != 0) {
            throw new IOException(err.toString());
        }

        return stdInput;
    } finally {
        if (stdError != null) {
            stdError.close();
        }
    }
}

所以,正如你所看到的,我调用了一个外部命令。然后读取错误流并在出现任何错误时抛出异常,否则我返回InputStream,所以我可以稍后解析它。

使用真正的SEAndroid策略,错误流似乎阻止(即使我读取单个字符),我无法解析命令的结果。如果我关闭错误流而不读取任何内容,应用程序工作正常,但我想处理错误,如果有的话。 如果我在控制台中键入命令,它也可以正常工作。

在第一种情况下(使用小型SEAndroid策略),命令的输出很小(约350行)。 在第二种情况下(使用真正的SEAndroid策略),命令的输出更大(> 1500行)。

输出流的大小是否可能影响错误流?这两个流是两个与众不同的资源,不是吗? 我没有立即读取输出流的事实具有重要性?

我担心这不是一个“编程”问题,而是一个系统问题......

有什么建议吗?

提前感谢您的帮助=)

修改

我尝试在错误流之前读取输出流并且它可以工作。但是我需要在对输出流执行任何解析之前检查错误流,所以问题仍然是热门话题。

2 个答案:

答案 0 :(得分:1)

首先,使用较新的ProcessBuilder类而不是Runtime exec可能更好。如果您想更进一步,您甚至可以使用Apache commons-exec来处理流处理和其他事情。

接下来,正如您所发现的那样,过程控制在Java中是一件棘手的事情,而且您遇到了一个棘手的问题。来自java的Process类的文档:

  

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

你需要有一些消耗(错误和输出)流的东西,否则你就有可能出现死锁 - 这些都应该在他们自己的线程上读取。使用像StreamGobbler这样的东西(google it,那里有很多东西)将是一个很好的步骤,或者你可以自己动手,如果你这么倾向。要想做到这一点并不困难,但如果你不熟悉多线程,你可能需要查看其他人的实现或者去Apache commons-exec路由。

答案 1 :(得分:0)

输出的处理非常烦人,我编写了一个名为jproc的小库来处理使用stdout和stderr的问题。它可以通过外部程序简单地过滤字符串,如下所示:

ProcBuilder.filter("x y z","sed" ,"s/y/a/")

它还允许您指定完成的超时,并将非零退出代码转换为异常。