如何从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程序无法知道{{1的顺序在子进程中调用。因此,如果两个流最终在同一个文件中,那么来自Java程序的stdout和stderr输出的顺序将明显不同。混合stdout和stderr当然不是解决方案,因为调用者可能希望将它们分开。
虽然我认为这个问题是普遍感兴趣的,但特定于Linux的解决方案可以解决我当前的问题。
答案 0 :(得分:2)
这是Java 7中引入的ProcessBuilder.redirectError
/ redirectOutput
的意图。使用Redirect.INHERIT
将使子进程与Java进程共享stderr / stdout:
class A {
public static void main(String[] args) {
try {
ProcessBuilder builder = new ProcessBuilder("echo", "foo");
builder.redirectError(ProcessBuilder.Redirect.INHERIT);
builder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
Process p = builder.start();
p.waitFor();
} catch (Exception e) {
System.err.println(e);
System.exit(1);
}
}
}
答案 1 :(得分:0)
你不能。默认情况下,子进程的所有标准I / O都被重定向到父进程(运行java程序的jvm)。
from the javadoc of the Process class:
默认情况下,创建的子进程没有自己的终端或 安慰。 所有标准I / O(即stdin,stdout,stderr) 操作将被重定向到父进程,它们可以在那里 通过使用getOutputStream()方法获得的流访问, getInputStream()和getErrorStream()。父进程使用这些 用于向子进程提供输入和输出的流。因为 某些本机平台仅为标准提供有限的缓冲区大小 输入和输出流,无法及时写入输入流 或者读取子进程的输出流可能会导致子进程 阻止,甚至死锁。
答案 2 :(得分:0)
您可以查看NuProcess项目。免责声明:我写了。它提供了衍生进程的非阻塞I / O.您仍然必须使用Java进行中继(您收到回调),但由于它在Linux的情况下使用epoll(),我希望它能保留底层程序的顺序。只有一个线程是epoll()管道,所以你不会得到任何线程调度订单问题。
我100%的订单将保留在MacOS X或任何BSD变体上,因为它使用了肯定有序的kqueue。无论如何,你可能想要试一试,编码和测试是微不足道的。