Runtime.getRuntime()。exec(“C:\ cygwin \ bin \ bash.exe”)没有输入读取

我正在尝试执行一个新进程并从Java中读取其输入流。 我已成功使用Runtime.getRuntime()。exec(String)来启动和接收来自多个进程的输入。 但是,当我尝试在某些其他进程上使用exec时,输入流的read方法会阻塞,并且看起来没有输入。 对于某些进程,可能导致输入流为空的原因是什么? 具体来说,我想知道为什么bash.exe没有输出任何东西。

我写了一个JUnit测试用例来演示这个问题:

import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import junit.framework.TestCase; public class TestExec extends TestCase { public void testExec() throws IOException { List threads = new ArrayList(); // Create a process for each of the commands and make sure that // it outputs at least one line to its input stream. threads.add(testExec("cmd")); threads.add(testExec("java")); threads.add(testExec("C:/cygwin/bin/vim-nox.exe")); // These bottom two fail, even though executing these // commands in cmd.exe results in immediate output threads.add(testExec("javac")); threads.add(testExec("C:/cygwin/bin/bash.exe")); // Give the threads a second to execute try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); fail(); } // Test that each command had input to read for(InputPrinter ip : threads) { assertTrue(ip.command + " has not read any input", ip.hasRead); } } // Starts a process for the given command and returns an // InputPrinter that can be used to check if the process // has had an input to read. public InputPrinter testExec(String command) throws IOException { Process proc = Runtime.getRuntime().exec(command); InputStream in = proc.getInputStream(); InputPrinter ip = new InputPrinter(in, command); new Thread(ip).start(); return ip; } // Simple Runnable to read from an InputStream. hasRead will be // true if at least one input has been read from the stream private class InputPrinter implements Runnable { InputStream in; String command; boolean hasRead; public InputPrinter(InputStream in, String command) { this.in = in; this.command = command; this.hasRead = false; } // Loop indefinitely while printing any received input public void run() { try { final byte[] b = new byte[1024]; while (true) { int n = in.read(b); if (n > 0) { System.out.print(new String(Arrays.copyOf(b, n))); hasRead = true; } } } catch (IOException e) { e.printStackTrace(); fail(); } } } } 

编辑:

据我所知,如果一个程序没有使用stdout或stderr,我不应该在windows命令提示符下看到任何内容。 当我启动bash进程时,我期待看到的是“bash-3.2 $”,当我打开命令提示符并运行“bash.exe”时,我看到了同样的事情:

 Microsoft Windows [Version 6.1.7600] Copyright (c) 2009 Microsoft Corporation. All rights reserved. C:\cygwin\bin>bash.exe bash-3.2$ 

无论Java如何,据我所知,只有当它作为脚本运行时才能从/向bash输出管道输出(或输入),而不是在它作为交互式shell运行时(在这种情况下你只能传递cmd参数)它)。

换句话说,当您在注释中提到从cmd运行bash时,您会看到输出,但它包含在bash进程中,而不会输出bash发送回父cmd进程的输出。

关于javac进程,它实际上是将输出发送到错误流。 尝试从cmd javac 1>nulljavac 2>null ,你会看到差异。
你看过这里的api吗? 您可以尝试使用ProcessBuilder并将错误流重定向回主输入流,以这种方式处理流程会更容易。

一个进程通常不仅有一个而是两个与之关联的输出流。 这些是:

  1. stdout,可以使用getInputStream()读取
  2. stderr,可以使用getErrorStream()读取

Javac写入stderr,而不是stdout,所以你不读它的输出。

因为不必阅读它们是不方便的(几年前,我不得不为此编写额外的线程),他们为系统进程引入了一个新的API,即ProcessBuilder,它允许将stderr重定向到stdout。

只需更换线条

  Process proc = Runtime.getRuntime().exec(command); InputStream in = proc.getInputStream(); 

  ProcessBuilder pb = new ProcessBuilder(command); pb.redirectErrorStream(true); Process proc = pb.start(); 

,添加所需的导入,您的测试成功:)。

Interesting Posts