从命令行读取输出

时间:2016-06-28 12:53:45

标签: java cmd io

我真的被困在这里,我已经在stackoverflow上阅读了一些文章和答案,但没有解决我的问题。

我已经使用批处理文件从cmd.exe运行Selenium Server Hub的方法:

   public static boolean startGrid() throws IOException {
        ProcessBuilder builder = new ProcessBuilder("cmd", "/c", "start", START_GRID_BAT_PATH);
        builder.redirectErrorStream(true);
        Process process = builder.start();
        String out = getCmdOutput(process);
        return out.contains("Selenium Grid hub is up and running");
    }

服务器已成功启动并在open cmd中运行。 为了确保服务器已启动,我使用以下方法从cmd获取输出:

   protected static String getCmdOutput(Process proc) throws java.io.IOException {
        String res = "";
        BufferedReader stdInput = new BufferedReader(new
                InputStreamReader(proc.getInputStream()));
        String s = "";
        while ((s = stdInput.readLine()) != null) {
            res += s;
        }
        return res;
    }

这就是问题开始的地方 - 方法挂在s = stdInput.readLine()) != null行 它似乎无法读取cmd中的任何行,但我可以看到运行服务器产生的输出中有多行。

任何想法有什么不对?

4 个答案:

答案 0 :(得分:0)

服务器产生的输出可能不包含任何\ r或\ n字符,但只是换行。

无论如何,在阅读输出时你不应该依赖换行符。

以下示例说明如何使用java.nio从非阻塞进程的InputStream读取二进制数据,将字节转换为字符并将结果写入StringWriter - 这更适合使用字符串连接。

protected static String getCmdOutput(Process proc, Consumer<String> consumer) 
  final InputStream source = proc.getInputStream();
  final StringWriter sink = new StringWriter();

  final ByteBuffer buf = ByteBuffer.allocate(1024);
  final CharBuffer chars = CharBuffer.allocate(1024);
  final CharsetDecoder decoder = Charset.defaultCharset().newDecoder();

  try(final ReadableByteChannel from = Channels.newChannel(source)) {
    while (from.read(buf) != -1) {
      buf.flip();
      decoder.decode(buf, chars, false);
      chars.flip();
      sink.write(chars.array(), chars.position(), chars.remaining());
      buf.compact();
      chars.clear();
    }
  } catch (IOException e) {
    e.printStackTrace();
  }
  return sink.toString();
}

修改 当您开始一个持续产生输出的过程时,只要过程正在运行,循环就不会完成。因此,要继续父进程,必须启动一个正在捕获并连续处理进程输出的线程

例如

Consumer<String> consumer = ...;
Thread t = new Thread(() -> {

  final InputStream source = proc.getInputStream();
  final StringWriter sink = new StringWriter();

  final ByteBuffer buf = ByteBuffer.allocate(1024);
  final CharBuffer chars = CharBuffer.allocate(1024);
  final CharsetDecoder decoder = Charset.defaultCharset().newDecoder();

  try(final ReadableByteChannel from = Channels.newChannel(source)) {
    while (from.read(buf) != -1) {
      buf.flip();
      decoder.decode(buf, chars, false);
      chars.flip();
      sink.write(chars.array(), chars.position(), chars.remaining());
      forward(sink, consumer); //forward the captured content to a consumer
      buf.compact();
      chars.clear();
    }
  } catch (IOException e) {
    e.printStackTrace();
  }
});
t.start();

private void forward(StringWriter sink, Consumer<String> consumer) {
  StringBuffer buf = sink.getBuffer();
  int pos = buf.lastIndexOf("\n"); 
  //you may use other options for triggering the process here,
  //i.e. on every invocation, on fixed length, etc
  if(pos != -1){
    consumer.accept(buf.substring(0, pos + 1));
    buf.delete(0, pos + 1);
  }
  //or to send the entire buffer
  /*
  consumer.accept(sink.toString());
  sink.getBuffer().delete(0, sink.getBuffer().length());
  */
}

根据您的使用情况,您可能不一定需要单独的线程。可以在主线程中处理子进程的输出并使用消费者的输出执行所需的操作。

答案 1 :(得分:0)

它没有挂起,只是阻止等待读取一行。读完线后,等待下一行。等等。

获取子进程的输出流应该在一个单独的线程中进行,因为子进程实际上可能会挂起,而主线程也会挂起。

一个例子:

给出实例方法

private static final long WAITTIME_FOR_CHILD = Duration.ofMinutes(5).toNanos();
private static final String START_SEQUENCE = "whatever";

private final Lock lock = new ReentrantLock();
private final Condition waitForStart = lock.newCondition();
private boolean started;

启动子进程的代码

// prepare child process
lock.lock();
try {
  Process process = processBuilder.start();
  // both standard and error output streams get redirected
  pipe(process.getInputStream(), System.out, START_SEQUENCE);
  pipe(process.getErrorStream(), System.err, START_SEQUENCE);
  // loop because of 'spurious wakeups' - should happen only on Linux,
  // but better take care of it
  // wait until WAITTIME_FOR_CHILD (default 5 minutes, see above)
  long waitTime = WAITTIME_FOR_CHILD;
  while (!started) {
    // we wait hier until the child process starts or timeout happens
    // value <= 0 means timeout
    waitTime = waitForStart.awaitNanos(waitTime);
    if (waitTime <= 0) {
      process.destroyForcibly();
      throw new IOException("Prozess xxx couldn't be started");
    }
  }
} catch (IOException | InterruptedException e) {
  throw new RuntimeException("Error during start of xxx", e);
} finally {
  lock.unlock();
}

private void getCmdOutput(InputStream in, PrintStream out, final String startSeq) {
  startDaemon(() -> {
    try (Scanner sc = new Scanner(in)) {
      while (sc.hasNextLine()) {
        String line = sc.nextLine();
        // check your know output sequence
        if (line.contains(startSeq)) {
          lock.lock();
          try {
            if (!started) {
              started = true;
              // waiting for process start finished
              waitForStart.signal();
            }
          } finally {
            lock.unlock();
          }
        }
        out.println(line);
      }
    }
  });
}

private void startDaemon(Runnable task) {
  Thread thread = new Thread(task);
  thread.setDaemon(true);
  thread.start();
}

答案 2 :(得分:0)

尝试像这样替换while条件:

while ((s = stdInput.readLine()) != null && !s.equals("")) {

答案 3 :(得分:-1)

 ProcessBuilder pb =
   new ProcessBuilder("myCommand", "myArg1", "myArg2");
 Map<String, String> env = pb.environment();
 env.put("VAR1", "myValue");
 env.remove("OTHERVAR");
 env.put("VAR2", env.get("VAR1") + "suffix");
 pb.directory(new File("myDir"));
 File log = new File("log");
 pb.redirectErrorStream(true);
 pb.redirectOutput(Redirect.appendTo(log));
 Process p = pb.start();
 assert pb.redirectInput() == Redirect.PIPE;
 assert pb.redirectOutput().file() == log;
 assert p.getInputStream().read() == -1;

您可以将输出重定向到oracle文档提供的基本示例中的文件:

https://docs.oracle.com/javase/7/docs/api/java/lang/ProcessBuilder.html