【发布时间】:2018-01-05 19:06:06
【问题描述】:
我有一个用 Java 编写的 GUI 程序,它使用 System.out.println 将数据输出到命令行。数据旨在通过管道传输到另一个程序中。作为示例,我将通过head 传递程序:
$ java MyProgram | head -n10
我希望我的程序在管道损坏时退出:在这种情况下,这应该发生在MyProgram 打印出十行文本之后。
有a similar question on this site,那里给出的解决方案效果很好;例如,我可以在自己的线程中运行以下内容:
while(!System.out.checkError()) {
// sleep 100ms
}
System.exit(0);
问题是PrintStream.checkError() 似乎只有在您尝试打印到流但失败后才返回true。出于这个原因,我的程序在打印出 11 行文本之前实际上并没有退出:在前十行之后管道被破坏,但 System.out 继续返回 true 直到我尝试管道通过第十一条线。
打印出额外的“垃圾”行以在PrintStream 上触发错误是不可能的,因为管道右侧的程序可能对其接收的数据非常敏感。
在循环内调用System.out.flush() 对PrintStream.checkError() 没有影响,尽管PrintStream 的源代码表明它应该调用该类中的私有ensureOpen 方法。
如何可靠地测试System.out 是否打开而不打印任何内容?
“真实世界”示例:假设我有一些程序consumer 接受命令行输入并对其进行处理。某些输入将调用consumer 静默失败。由于consumer 的输入有时很长而且很深奥,我用Java 编写了一个GUI 程序InputProvider,我可以在其中单击按钮并将相应的命令打印到stdout。如果我将InputProvider 的输出通过管道传输到consumer,那么我就能够以图形方式控制consumer。
不幸的是,InputProvider 似乎无法在consumer 关闭时通知用户,除非尝试写信给consumer 并获得某种异常。
【问题讨论】:
-
这听起来像XY Problem。你需要澄清为什么你认为在第 11 行输出失败是不行的。所有这一切发生的底层机制是在操作系统中,而不是在 Java 中,并且在您尝试编写之前不会发生损坏的管道指示。
-
@JimGarrison 我认为这不是 XY 问题。当管道另一侧的程序关闭时,我希望我的输入程序关闭。也许
head示例具有误导性。至少,我希望我的输入程序能够判断其他程序是否已经退出。两者通信的唯一机制是管道,对应System.out的状态。 -
那么正如我所说,如果没有其他沟通渠道,这是不可能的。管道由操作系统处理,在您尝试写入管道之前不会发生错误。换句话说,尝试使用损坏的管道进行流量控制是一种不正确的方法,并且无法正常工作。
-
你能详细说明一下,你怎么能说 10 行后管道坏了?看来您有办法通过查看输出行来了解管道是否损坏。第 11 行出现在输出中的事实意味着 IO 在此之前是完整的。
-
@svasa 第 11 行没有出现在输出中,但
MyProgram没有退出。在这种情况下,这不是问题,因为我可以看出head已经退出,因为它不再产生输出。但是,对于另一个不直接打印其输入的程序,我可能无法判断该程序已经退出。