【问题标题】:Error stream blocking when running external command with Java使用 Java 运行外部命令时出现错误流阻塞
【发布时间】:2014-06-08 13:16:50
【问题描述】:

在 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 行)。

输出流的大小是否会影响错误流?这两个流是两种不同的资源,不是吗? 我没有立即读取输出流这一事实很重要吗?

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

有什么建议吗?

提前感谢您的帮助=)

编辑:

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

【问题讨论】:

    标签: java stream runtime.exec


    【解决方案1】:

    首先,与 Runtime exec 相比,使用较新的 ProcessBuilder 类可能会更好。如果你想更进一步,你甚至可以使用Apache commons-exec,它会为你处理流处理和其他事情。

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

    父进程使用这些流向子进程提供输入并从子进程获取输出。由于部分原生平台只为标准输入输出流提供有限的缓冲区大小,未能及时写入子进程的输入流或读取输出流可能导致子进程阻塞,甚至死锁。

    您需要同时消耗(错误和输出)流的东西,否则您将面临死锁的风险 - 这些都应该在各自的线程上读取。使用类似 StreamGobbler 的东西(谷歌搜索,那里有很多)将是一个很好的步骤,或者如果你愿意的话,你可以自己动手。把它做好并不难,但如果你不熟悉多线程,你可能想看看别人的实现或走 Apache commons-exec 路线。

    【讨论】:

    • 谢谢。我使用带有 StreamGobbler 的 processBuilder,它可以工作。我什至不需要更改我的应用程序中的任何其他内容。我使用了我找到的代码here。你有什么好的 StreamGobbler 实现建议吗?
    • 我相信当我遇到同样的问题时,我为我的模板使用了相同的代码。我对其进行了一些更改以满足我的需要,但底层结构是相同的。
    • 我提供的链接中的 StreamGobbler 不是很优化。实际上,它执行流内容的两个副本...对于大流不是很有效。
    【解决方案2】:

    输出的处理太烦人了,我写了一个叫做jproc的小库来处理消耗stdout和stderr的问题。它可以简单地通过外部程序过滤字符串,如下所示:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-07-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-08
      • 2013-01-10
      • 1970-01-01
      相关资源
      最近更新 更多