【问题标题】:design for a wrapper around command-line utilities围绕命令行实用程序的包装设计
【发布时间】:2010-03-17 15:19:23
【问题描述】:

我正在尝试设计一个包装器,以便在 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") 实现一个漂亮的包装器有什么更好的想法吗?

【问题讨论】:

    标签: java command-line runtime.exec


    【解决方案1】:

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

    【讨论】:

    • 不完全是我想要的,但可能与我想要的一样接近。谢谢
    • 我对这个库没有任何疑问,因为它可以在 Maven 2 中使用,所以我会说它是最好的。
    • 我没有暗示任何关于代码质量的意思。我只是说它不是为进程和 java 代码之间的任何真正交互而设计的——只是为了运行外部进程并返回一个结果
    • 英语不是我的主要语言,但问题实际上是关于这一点的:“关于我如何围绕 runtime.exec("cmd.exe") 实现一个好的包装器有什么更好的想法吗?” :)
    • PlexusUtils(或任何其他库)的目的是提供一种透明的方式来获取进程的输出。您仍然可以获取 Process 对象并将数据注入到调用的进程中:java.sun.com/j2se/1.5.0/docs/api/java/lang/…
    【解决方案2】:

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

    【讨论】:

    • 很有趣,但它似乎无法处理错误。如果我“期望”运行某个输出并打印出一些错误,则只有在超时时才知道...
    【解决方案3】:

    我会通过流读取输入,然后将其写入 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();
        }
    }
    

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

    【讨论】:

    • 您正在考虑一次性进程执行,我在其中运行命令,等待其输出,仅此而已。我希望能够处理更复杂的命令行应用程序,例如 wget 等,这些应用程序可能会在相对较长的时间内不写任何东西,并且可能不是一次性的
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-10
    • 2020-02-22
    • 1970-01-01
    相关资源
    最近更新 更多