从批处理文件的进程读取InputStream会跳到下一行

时间:2012-02-12 18:56:24

标签: java windows process batch-file inputstream

我正在尝试使用Runtime.exec()运行批处理文件,然后将其InputStream输出到JTextArea。我有什么作品,但只是部分。批处理文件运行会发生什么,但如果它执行除“echo”之外的命令,该命令会立即终止并执行下一行。例如,假设我尝试运行这样一个简单的批处理文件:

@echo off
echo hello. waiting 5 seconds.
timeout /t 5 /nobreak > NUL
echo finished. goodbye.

批处理文件执行,JTextArea表示

hello. waiting 5 seconds.
finished. goodbye.

但它不会在中间等待5秒钟。

我无法弄清楚为什么会这样做。这是我用来运行批处理文件并读取其InputStream的内容。

private class ScriptRunner implements Runnable {
private final GUI.InfoGUI gui; // the name of my GUI class
private final String script;

public ScriptRunner(final GUI.InfoGUI gui, final File script) {
    this.gui = gui;
    this.script = script.getAbsolutePath();
}

@Override
public void run() {
    try {
        final Process p = Runtime.getRuntime().exec(script);
        StreamReader output = new StreamReader(p.getInputStream(), gui);
        Thread t = new Thread(output);
        t.start();
        int exit = p.waitFor();
        output.setComplete(true);
        while (t.isAlive()) {
            sleep(500);
        }
        System.out.println("Processed finished with exit code " + exit);
    } catch (final Exception e) {
        e.printStackTrace();
    }
}

}
private class StreamReader implements Runnable {
private final InputStream is;
private final GUI.InfoGUI gui;

private boolean complete = false;

public StreamReader(InputStream is, GUI.InfoGUI gui) {
    this.is = is;
    this.gui = gui;
}

@Override
public void run() {
    BufferedReader in = new BufferedReader(new InputStreamReader(is));
    try {
        while (!complete || in.ready()) {
            while (in.ready()) {
                gui.setTextAreaText(in.readLine() + "\n");
            }
            sleep(250);
        }
    } catch (final Exception e) {
        e.printStackTrace();
    } finally {
            try {
                in.close();
        } catch (final Exception e) {
                e.printStackTrace();
    }
}

public void setComplete(final boolean complete) {
    this.complete = complete;
}

}
public void sleep(final long ms) {
try {
    Thread.sleep(ms);
} catch (final InterruptedException ie) {
}
}

我知道我的代码非常混乱,我确信它包含语法错误。

感谢您提供帮助的任何事情!

4 个答案:

答案 0 :(得分:3)

您正在创建Process,但您没有从标准错误流中读取。该过程可能是将消息写入其标准错误以告诉您存在问题,但如果您没有阅读其标准错误,则无法阅读这些消息。

这里有两个选项:

  1. 由于您已经有一个从流(StreamReader)读取的类,因此将其中一个连接到流程的标准错误流(p.getErrorStream())并在另一个{Thread中运行它{1}}。当调用setComplete返回时,您还需要在错误StreamReader上调用p.waitFor(),并等待运行它的线程死亡。

  2. Runtime.getRuntime().exec()替换为ProcessBuilder。此类是Java 5中的新增类,它提供了另一种运行外部进程的方法。在我看来,它比Runtime.getRuntime().exec()最显着的改进是将流程的标准错误重定向到其标准输出的能力,因此您只能读取一个流。

  3. 我强烈建议您选择第二个选项并选择将流程的标准错误重定向到其标准输出中。

    我拿了你的代码并替换了

            final Process p = Runtime.getRuntime().exec(script);
    

            final ProcessBuilder pb = new ProcessBuilder(script);
            pb.redirectErrorStream(true);
            final Process p = pb.start();
    

    另外,我没有您的GUI代码,所以我将流程的输出写入System.out

    当我运行你的代码时,我得到了以下输出:

    hello. waiting 5 seconds.
    ERROR: Input redirection is not supported, exiting the process immediately.
    finished. goodbye.
    Processed finished with exit code 0
    

    如果您看到该错误消息,您可能会发现timeout命令出现问题。

    顺便说一下,我在你的一条评论中注意到ughzan建议的命令都没有奏效。我将timeout行替换为ping -n 5 127.0.0.1 > NUL,脚本按预期运行。我无法重现这个问题。

答案 1 :(得分:2)

问题肯定在timeout.exe。如果您在使用echo %errorlevel%后添加timeout,则会在从java运行时看到它返回1。如果以通常的方式运行,0。可能需要一些特定的控制台功能(即光标定位),这些功能在从java进程运行时被禁止。

  

从Java

运行时,我能做些什么才能让它工作

如果您不需要运行任何批处理文件,请考虑将timeout替换为ping。否则......我尝试使用JNA通过Kernel32.CreateProcess运行批处理文件,timeout运行正常。但是,您还需要通过本机调用来实现对流程输出的读取。

我希望有人能提出更好的方法。

答案 2 :(得分:1)

ready方法仅告知流是否可以保证可以立即读取某些内容而不会阻塞。你不能真正信任它,因为总是返回false是一个有效的实现。具有缓冲区的流只有在缓冲时才会返回true。所以我怀疑你的问题在这里:

    while (!complete || in.ready()) {
        while (in.ready()) {
            gui.setTextAreaText(in.readLine() + "\n");
        }
        sleep(250);
    }

它应该是这样的:

    String line;
    while (!complete || (line=in.readLine()) != null) {
        gui.setTextAreaText(line + "\n");
    }

答案 3 :(得分:1)

这可能是因为你的“timeout ...”命令返回错误。

测试它的三种方法:

  1. 检查“timeout ...”命令是否在Windows命令提示符下有效。
  2. 在脚本中用“ping -n 5 127.0.0.1> NUL”替换“timeout ...”(它基本上做同样的事情)
  3. 从脚本中删除除“timeout / t 5 / nobreak> NUL”之外的所有内容。如果超时失败,则进程应返回错误(1),因为它是最后执行的命令。