【发布时间】:2014-03-13 00:13:18
【问题描述】:
如何从 Java 程序运行外部命令(通过 shell),这样就不会发生重定向,并等待命令结束?我希望外部程序的文件描述符与 Java 程序的文件描述符相同。特别是 我不希望将输出重定向到 Java 程序正在读取的管道。让 Java 程序中继输出不是解决方案。
这意味着对java.lang.Runtime.exec 的简单调用不是解决方案。我假设涉及java.lang.ProcessBuilder,但是如何指定输出和错误流必须与调用Java进程相同?
class A {
public static void main(String[] args) {
try {
ProcessBuilder pb = new ProcessBuilder("echo", "foo");
/*TODO: pb.out = System.out; pb.err = System.err;*/
Process p = pb.start();
p.waitFor();
} catch (Exception e) {
System.err.println(e);
System.exit(1);
}
}
}
(这可能是正确的方法,也可能不是。)
换句话说,我正在寻找 Java 的 system,但我只能找到(大致)popen。
下面是一个中继不能工作的例子:如果子进程同时写入stdout和stderr并且Java程序正在中继,那么Java程序无法知道子进程中write调用的顺序.因此,如果两个流最终位于同一个文件中,那么 Java 程序在 stdout 和 stderr 上的输出顺序将明显不同。混合 stdout 和 stderr 当然不是解决方案,因为调用者可能希望将它们分开。
虽然我认为这个问题引起了人们普遍的兴趣,但特定于 Linux 的解决方案可以解决我的直接问题。
【问题讨论】:
-
你应该使用
Process.getInputStream()。您需要在两个单独的线程中从 sysout 和 err 读取进程,否则它们的缓冲区可能会填满并且进程将阻塞。 This 是一篇关于从 Java 运行外部进程的优秀文章。就我个人而言,我会使用Apache commons exec 来避免自己的痛苦。 -
@BoristheSpider 我不想从 Java 程序中读取子进程的输出。
-
如果您不想中继输出,并且满足于坚持特定于平台的解决方案,您可以使用 jni 调用包装器,该包装器反过来又调用平台的本机系统() 功能。但是,请记住 system() 是阻塞的,因此您可能需要从它自己的线程中执行此操作。不过,您可能会遇到各种与 exec() 相关的复杂情况,具体取决于 JVM 的设置方式。另一种选择是找出当前终端是什么,并在子进程中将其作为标准输出打开,可能使用中介。
-
@ChrisStratton 终端?检测到虚假假设。是的,JNI 将是一种解决方案,由于复杂性,我不愿意追求。作为一个习惯性的 Java 程序员,我觉得很奇怪这种具有非常丰富和复杂的库的语言似乎没有解决这个简单任务的方法。
-
然后不要使用终端,但无论当前输出是什么 - 在 linux 上,如果没有其他内容,您可以从 /proc/self/fd 中的链接中找到它。更根本的是,有一个标准的 java 解决方案可以解决这个问题,但您拒绝了它,因为您错误地认为您想要做的事情的方式会有所不同。
标签: java exec file-descriptor