设计命令行实用程序的包装器

时间:2010-03-17 15:19:24

标签: java command-line runtime.exec

我试图想出一个包装器的设计,以便在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”)之类的东西上实现一个漂亮的包装器的任何更好的想法?

3 个答案:

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

这将确保您获取最后一行输入,它解决了您知道流何时结束的问题。