简体   繁体   English

从命令行读取输出

[英]Reading output from command line

I'm really stuck here, I've read number of articles and answers on stackoverflow, but nothing solved my problem. 我真的被困在这里,我已经阅读了许多关于stackoverflow的文章和答案,但是没有什么能解决我的问题。

I've got the method which runs Selenium Server Hub from cmd.exe using batch file: 我已经有了使用批处理文件从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");
    }

The server is started successfully and is running in open cmd. 服务器已成功启动,并在打开的cmd中运行。 To make sure that server is up I use the following method to get the output from the 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;
    }

And here is where the problem starts - method hangs at the the line s = stdInput.readLine()) != null It seems that it can't read any line from the cmd, although I can see that there is number of lines in the output produced by running server. 这是问题开始的地方-方法挂在行s = stdInput.readLine()) != null似乎无法从cmd读取任何行,尽管我可以看到其中有很多行运行服务器产生的输出。

Any ideas what is wrong? 任何想法有什么问题吗?

Maybe the output produced by the server doesn't contain any \\r or \\n characters but is simply line-wrapped. 服务器产生的输出也许不包含任何\\ r或\\ n字符,而只是换行了。

Anyhow, you shouldn't rely on the line feed characters when reading the output. 无论如何,在读取输出时,您不应该依赖换行符。

The following example shows how to read binary data from the InputStream of the process non-blocking with java.nio, convert the bytes to chars and write the result to a StringWriter - which is preferrable to using string concatenation. 下面的示例演示如何从使用java.nio进行非阻塞处理的InputStream中读取二进制数据,如何将字节转换为char并将结果写入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();
}

Edit: As you are starting a process that is continuously producing output, the loop won't finish as long as the process is running. 编辑:当您启动一个持续产生输出的进程时,只要进程正在运行,循环就不会结束。 So to continue your parent process, you have to start a thread that is capturing and processing the output of the process continuously 因此,要继续父进程,您必须启动一个线程,该线程正在连续捕获和处理该进程的输出

For example 例如

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());
  */
}

Depending on your use case, you may not necessarily need a separate thread. 根据您的用例,您不一定需要一个单独的线程。 It might be ok to process the child-process' output in the main thread and do what you want with the output in the consumer. 可以在主线程中处理子进程的输出,然后使用使用者中的输出来做您想做的事情。

It doesn't hang, it just blocks waiting for a line to be read. 它不会挂起,它只是阻止等待一行被读取。 And after the line has been read, it waits for the next. 在读取该行之后,它等待下一行。 And so on. 等等。

Getting the output stream of a child process should be made in a separate thread, because the child process could in fact hang, than your main thread will hang, too. 获取子进程的输出流应该在单独的线程中进行,因为实际上子进程可能会挂起,而主线程也会挂起。

An example: 一个例子:

given the instance methods 给定实例方法

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;

the code that starts the child process 启动子进程的代码

// 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();
}

尝试更换while条件是这样的:

while ((s = stdInput.readLine()) != null && !s.equals("")) {
 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;

You can redirect output to a file as oracle documentation offers in basic example: 您可以将输出重定向到oracle文档在基本示例中提供的文件:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM