【问题标题】:BufferedReader.read() hangs when running a perl script using Runtime.exec()使用 Runtime.exec() 运行 perl 脚本时,BufferedReader.read() 挂起
【发布时间】:2016-02-28 11:05:50
【问题描述】:

我正在尝试从 Java 代码运行 perl 脚本并使用以下代码读取它的输出:

String cmd = "/var/tmp/./myscript";
Process process = Runtime.getRuntime().exec(cmd);
stdin = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while((line = stdin.readLine()) != null) {
    System.out.println(line);
}

但代码总是挂在 readLine() 上。

我尝试过使用

stdin.read();

相反,但这也会挂起。 尝试将cmd修改为

cmd = "perl /var/tmp/myscript";

还有

cmd = {"perl","/var/tmp/myscript"};

但这也挂了。 尝试在单独的线程中读取标准输入。尝试在单独的线程中读取标准输入和标准错误。还是没有运气。

我知道这里有很多问题处理 Process.waitFor() 由于未读取流而挂起,以及 BufferedReader.read() 挂起,尝试了所有建议的解决方案,仍然没有运气。

当然,在 CLI 本身上运行相同的脚本会将输出写入标准输出(控制台)并以退出代码 0 存在。 我在 Centos 6.6 上运行。

我们将不胜感激。

【问题讨论】:

  • 你的 perl 程序是否只产生输出?还是需要任何输入?要求“按返回”继续?
  • 无需输入。如前所述,相同的命令在终端中执行就好了。

标签: java command-line-interface


【解决方案1】:

我假设当直接从命令行运行时,脚本会运行到完成,产生预期的输出,然后干净地终止。如果没有,请先修复您的脚本。

readLine() 调用挂起几乎可以肯定意味着既没有遇到行终止符也没有遇到文件结尾。换句话说,该方法在等待脚本时被阻塞。也许脚本在条件下根本不产生任何输出,但不会终止。例如,如果它希望在继续之前从自己的标准输入中读取数据,则可能会发生这种情况。如果它在输出到其 stderr 时被阻塞,也可能发生这种情况。

在一般情况下,您必须通过getInputstream()getErrorStream() 提供的InputStreams 并行读取Process 的标准输出及其标准错误。您还应该处理getOutputStream() 提供的OutputStream,方法是向其提供所需的标准输入数据(也与读取并行)或关闭它。如果您正在运行的特定进程没有向这些流发送数据,您可以用关闭进程的流来代替读取它们,并且您通常应该在没有时关闭ProcessOutputStream更多数据。您需要阅读这两个InputStreams,即使您不关心从它们中读取的内容,因为如果您不这样做,该过程可能会阻塞或无法终止。这很难做到正确,但对于特定情况来说比编写通用支持更容易。无论如何,有ProcessBuilder,它在某种程度上朝着更简单的通用接口方向发展。

【讨论】:

  • 谢谢@John Bollinger。关闭标准输入(通过 getOutputStream() 提供的 OutputStream)解决了这个问题。稍后我将发布固定代码以供公众使用。
【解决方案2】:

尝试像这样使用ProcessBuilder

String cmd = "/var/tmp/./myscript";

ProcessBuilder perlProcessBuilder = new ProcessBuilder(cmd);
perlProcessBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE);
Process process = perlProcessBuilder.start();

stdin = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while((line = stdin.readLine()) != null) {
    System.out.println(line);
}

来自 ProcessBuilder javadoc (link)

public ProcessBuilder redirectOutput(ProcessBuilder.Redirect destination)

设置此流程构建器的标准输出目标。随后由该对象的start() 方法启动的子进程将其标准输出发送到此目的地。

如果目标是Redirect.PIPE(初始值),则可以使用Process.getInputStream() 返回的输入流读取子进程的标准输出。如果目标设置为任何其他值,则Process.getInputStream() 将返回一个空输入流。

参数:

destination - 新的标准输出目的地

返回:

此流程构建器

投掷:

IllegalArgumentException - 如果重定向不对应于数据的有效目的地,即具有 READ 类型

自:

1.7

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-19
    • 2014-11-24
    • 2018-12-13
    • 2014-09-23
    • 2011-09-23
    • 2020-09-15
    • 1970-01-01
    相关资源
    最近更新 更多