我试图想出一个包装器的设计,以便在java中调用命令行实用程序时使用。 runtime.exec()的问题在于你需要继续读取进程的out和err流,否则它会在填充缓冲区时挂起。这导致我采用以下设计:
public class CommandLineInterface {
private final Thread stdOutThread;
private final Thread stdErrThread;
private final OutputStreamWriter stdin;
private final History history;
public CommandLineInterface(String command) throws IOException {
this.history = new History();
this.history.addEntry(new HistoryEntry(EntryTypeEnum.INPUT, command));
Process process = Runtime.getRuntime().exec(command);
stdin = new OutputStreamWriter(process.getOutputStream());
stdOutThread = new Thread(new Leech(process.getInputStream(), history, EntryTypeEnum.OUTPUT));
stdOutThread.setDaemon(true);
stdOutThread.start();
stdErrThread = new Thread(new Leech(process.getErrorStream(), history, EntryTypeEnum.ERROR));
stdErrThread.setDaemon(true);
stdErrThread.start();
}
public void write(String input) throws IOException {
this.history.addEntry(new HistoryEntry(EntryTypeEnum.INPUT, input));
stdin.write(input);
stdin.write("\n");
stdin.flush();
}
}
和
public class Leech implements Runnable{
private final InputStream stream;
private final History history;
private final EntryTypeEnum type;
private volatile boolean alive = true;
public Leech(InputStream stream, History history, EntryTypeEnum type) {
this.stream = stream;
this.history = history;
this.type = type;
}
public void run() {
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
String line;
try {
while(alive) {
line = reader.readLine();
if (line==null) break;
history.addEntry(new HistoryEntry(type, line));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
我的问题在于Leech类(用于“挖掘”流程'out and err streams并将它们提供给历史记录 - 这就像一个日志文件) - 一方面阅读整行很简单(和我目前正在做什么),但这意味着我错过了最后一行(通常是提示行)。我只在执行下一个命令时看到提示行(因为在该点之前没有换行符)。 另一方面,如果我自己读取字符,我怎么知道这个过程何时“完成”? (完成或等待输入) 自从过程的最后一个输出并声明它“完成”以来,有没有人尝试过等待100毫秒的东西?
关于如何在runtime.exec(“cmd.exe”)之类的东西上实现一个漂亮的包装器的任何更好的想法?
答案 0 :(得分:2)
使用PlexusUtils Apache Maven 2使用它来执行所有外部进程。
答案 1 :(得分:1)
我自己也在寻找同样的东西,我找到了一个名为ExpectJ的Expect Java端口。我还没有尝试过,但看起来很有希望
答案 2 :(得分:0)
我会用流读取输入,然后将其写入ByteArrayOutputStream。字节数组将继续增长,直到不再有任何可读字节为止。此时,您将通过将字节数组转换为字符串并将其拆分到平台line.separator上来将数据刷新到历史记录。然后,您可以遍历这些行以添加历史记录条目。然后重置ByteArrayOutputStream并且while循环阻塞,直到有更多数据或到达流的末尾(可能是因为该过程已完成)。
public void run() {
ByteArrayOutputStream out = new ByteArrayOutputStream();
int bite;
try {
while((bite = stream.read()) != -1) {
out.write(bite);
if (stream.available() == 0) {
String string = new String(out.toByteArray());
for (String line : string.split(
System.getProperty("line.separator"))) {
history.addEntry(new HistoryEntry(type, line));
}
out.reset();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
这将确保您获取最后一行输入,它解决了您知道流何时结束的问题。