[英]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.