2

我试图设计一个包装器,以便在 java 中调用命令行实用程序时使用。runtime.exec() 的问题在于,您需要不断地从进程中读取数据和错误流,否则它会在填充缓冲区时挂起。这导致我进行以下设计:

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 类(用于“提取”进程和错误流并将它们输入到历史记录中 - 这就像一个日志文件) - 一方面阅读整行很好而且很容易(以及我目前的做),但这意味着我错过了最后一行(通常是提示行)。我只在执行下一个命令时看到提示行(因为在那之前没有换行符)。另一方面,如果我自己阅读字符,我如何判断该过程何时“完成”?(完成或等待输入)有没有人尝试过自进程的最后输出以来等待 100 毫秒并声明它“完成”之类的事情?

关于我如何围绕诸如 runtime.exec("cmd.exe") 之类的东西实现一个好的包装器有什么更好的想法吗?

4

3 回答 3

3

使用Apache Maven 2 使用的PlexusUtils来执行所有外部进程。

于 2010-03-23T18:35:25.287 回答
1

我自己也在寻找同样的东西,我发现了一个名为ExpectJ的 Expect 的 Java 端口。我还没有尝试过,但看起来很有希望

于 2010-03-21T20:29:40.193 回答
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();
    }
}

这将确保您选择最后一行输入,并解决您知道流何时结束的问题。

于 2010-03-23T17:14:29.353 回答