2014-03-13 49 views
2

如何从Java程序运行外部命令(通过shell),以便不发生重定向,并等待命令结束?我希望外部程序的文件描述符与Java程序的文件描述符相同。特别是我不希望输出被重定向到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

这里是哪里中继无法工作情况的一个例子:如果子进程写到标准输出和标准错误和Java程序中继,那么Java程序没有办法知道在子的write调用的顺序。因此,如果两个流最终在同一个文件中,那么来自Java程序的stdout和stderr上的输出顺序将明显不同。混合stdout和stderr当然不是一个解决方案,因为调用者可能希望将它们分开。

虽然我认为这个问题很普遍,但Linux特有的解决方案可以解决我眼前的问题。

+0

您应该使用['Process.getInputStream( )'](http://docs.oracle.com/javase/7/docs/api/java/lang/Process.html#getInputStream())。您需要从sysout和err中读取两个独立线程中的进程,否则它们的缓冲区可能会被填满,进程将被阻止。 [This](http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html)是一篇关于从Java运行外部进程的优秀文章。就我个人而言,我会使用[Apache commons exec](http://commons.apache.org/proper/commons-exec/)来节省自己的一些痛苦。 –

+0

@BoristheSpider我不想从Java程序中读取子进程的输出。 – Gilles

+0

如果您不想中继输出,并且您满足于坚持特定于平台的解决方案,那么您可以使用jni调用一个包装器,然后调用该平台的本地系统()函数。但是,请记住system()是阻塞的,所以您可能需要从它自己的线程中完成此操作。这可能会遇到各种exec()相关的复杂问题,具体取决于JVM如何设置。另一个选择是找出当前终端是什么,并在子进程中打开stdout,可能使用中介。 –

回答

2

这是是在Java 7中引入的使用​​将使ProcessBuilder.redirectError/redirectOutput意图子进程共享标准错误/标准输出与Java进程:

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); 
     } 
    } 
} 
0

你不能。默认情况下,子进程的所有标准I/O都被重定向到父进程(运行Java程序的jvm)。

from the javadoc of the Process class

默认情况下,创建的子进程没有自己的终端或 控制台。 所有它的标准I/O(即标准输入,标准输出,标准错误) 操作将被重定向到父进程,在那里他们可以是 经由使用方法的getOutputStream(), 的getInputStream()中获得的流访问,并且getErrorStream( )。父进程使用这些 流将输入提供给子进程并从子进程获取输出。由于 某些本机平台仅为标准的 输入和输出流提供有限的缓冲区大小,未能及时写入输入流 或读取子流程的输出流可能导致子流程 阻塞甚至死锁。

+0

我知道,默认情况下他们正在重定向。我该如何改变它? – Gilles

+0

其实我的不好。该javadoc的兴趣点是“创建的子进程没有自己的终端或控制台”。使用“默认”,在javadoc中是相当糟糕的选择。它只是意味着,这是唯一可能的方式(就在终端或控制台运行而言)。 – ylabidi

+0

我不想创建一个终端 - 我不是在寻找'expect'。我希望子进程的输出到达Java进程输出的任何位置。 – Gilles

0

您可能会看看NuProcess项目。免责声明:我写了。它提供来自产生的进程的非阻塞I/O。你仍然需要在Java中进行中继(你会收到回调),但是因为它在Linux中使用epoll(),所以我期望它能够保留底层程序的顺序。只有一个线程是epoll()管道,所以你不会得到任何线程调度顺序问题。

我100%订单将保留在MacOS X或任何BSD变种,因为它使用肯定订购的kqueue。无论如何,你可能想要试一试,编码和测试是微不足道的。

相关问题