在Java中执行外部命令的经典问题?

时间:2011-03-05 18:25:24

标签: java redirect stream command external

好的 - 这对很多人来说都是一个问题 - 因为我还没有看到一个有效的答案,我想我会表达这个问题,所以找到它的人可以告诉我们其他人

问题是以下三个中的两个工作只是很好的相同代码。

reader3的实例演示了这个问题。 Reader3无法读取成功启动外部文件的结果。尝试在stdin或stderr InputStream上永久阻止任何类型的读取(realine等):

package Problems;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class RunningProblem {

public static class RunningReader implements Runnable {

    private Process proc;
    private String sName;

    private RunningReader(Process proc1, String sName) {
        this.proc = proc1;
        this.sName = sName;
    }

    public void run() {
        try {                
            // InputStreamReader in = new InputStreamReader(proc.getInputStream());
            // BufferedReader reader = new BufferedReader(in);

            InputStreamReader err = new InputStreamReader(proc.getErrorStream());
            BufferedReader reader = new BufferedReader(err);

            String line = reader.readLine();
            while (line != null) {
                System.out.println(sName + ": " + line);
                line = reader.readLine();
            }
            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public static void main(String[] args) {
    ExecutorService pool = Executors.newFixedThreadPool(3);
    try {
        Runtime rt = Runtime.getRuntime();

        Process proc1 = rt.exec("ps ax");
        RunningReader reader1 = new RunningReader(proc1, "reader1");

        Process proc2 = rt.exec("ls -l /");
        RunningReader reader2 = new RunningReader(proc2, "reader2");

        Process proc3 = rt.exec("/bin/tar");
        RunningReader reader3 = new RunningReader(proc3, "reader3");

        pool.execute(reader3);
        pool.execute(reader2);
        pool.execute(reader1);

    } catch (Exception ex) {
        System.err.println(ex.getMessage());
    } finally {
        pool.shutdown();
    }
    System.out.println("Launcher.main() Exited.");
}

}

4 个答案:

答案 0 :(得分:1)

您没有显示程序产生的输出,但我想可能是这种情况,ps axls -l /不会产生输出,但/bin/tar会产生输出。原因是,前两个命令产生的输出为stdout但不产生stderr,而后者将产生stderr的输出(因为您没有给{{1}的有效参数})但不在tar上。

这是在shell中运行命令时的区别:

stdout

使用[axe@gromp tmp]$ ps ax > ps-std.txt 2> ps-err.txt [axe@gromp tmp]$ ls -l / > ls-std.txt 2> ls-err.txt [axe@gromp tmp]$ /bin/tar > tar-std.txt 2> tar-err.txt [axe@gromp tmp]$ ls -lrt total 18 -rw-r--r-- 1 axe users 0 Mar 5 19:40 ps-err.txt -rw-r--r-- 1 axe users 7191 Mar 5 19:40 ps-std.txt -rw-r--r-- 1 axe users 0 Mar 5 19:40 ls-err.txt -rw-r--r-- 1 axe users 937 Mar 5 19:40 ls-std.txt -rw-r--r-- 1 axe users 0 Mar 5 19:41 tar-std.txt -rw-r--r-- 1 axe users 142 Mar 5 19:41 tar-err.txt [axe@gromp tmp]$ 重定向标准输出,>将错误输出重定向到不同的文件,您可以看到2>tar和其他两个上生成消息在stderr上(其他文件的文件大小为零,没有输出)。

可能是这种情况吗?如果你跑步会发生什么,例如G。 stdout代替echo "Foo"作为第三个流程?

答案 1 :(得分:1)

我在我的系统上运行了你的代码,在正常退出之前它给了我以下输出:

reader3: /bin/tar: You must specify one of the `-Acdtrux' options
reader3: Try `/bin/tar --help' or `/bin/tar --usage' for more information.
Launcher.main() Exited.

ps axls -l /没有输出,但是从shell运行它们确认它们没有写任何标准错误。在我的系统上,你的代码碰巧正常完成,但我可以想象这种情况不会发生。请注意,如果进程在其标准输出上生成大量输出,则缓冲区可能会填满,这会导致进程挂起。

我建议使用ProcessBuilder代替。{ Runtime.getRuntime().exec("...")。首先,它允许您将标准错误流重定向到标准输出流,然后不必担心要读取的两个流中的哪一个。

答案 2 :(得分:0)

Facinating(spockian eyebrow) - 从上面的评论来看,它看起来有很多理由说明为什么这么多人都有这方面的问题 - 我们的实现似乎各不相同!

我正在运行Ubuntu。有趣的是,使用ProcessBuilder颠倒了问题...但是至少现在没有人在使用它时“永远阻止”。 - 至少stderr和stdin能够被阅读!

所以对我来说,现在,经验法则似乎是:在Ubuntu上使用“旧方式”(Runtime.getRuntime()。exec())来命令shell('内部')命令(Oracle / Sun VM) - 使用ProcessBuilder获取外部命令(如tar等):

Process proc3 = new ProcessBuilder("/bin/tar").start();
RunningReader reader3 = new RunningReader(proc3, "reader3");

...

  

reader3:/ bin / tar:你必须指定   其中一个-Acdtrux' options reader3: Try / bin / tar --help'或`/ bin / tar   --usage'了解更多信息。

- 对于我们中的许多人来说,这是一项非常重要的操作......将一个矩阵放在哪个平台上的什么用途上会很好.... (也就是说我想知道OpenJDK在Ubuntu上会做得更好吗?)

答案 3 :(得分:-1)

你可能很晚才开始使用RunningReaders。 您启动了三个进程,在开始阅读RunningReader中的Error-OutputStream之前,有时可能已完成第一个进程。

必须在流程开始后立即启动RunningReaders。

在某些条件下,即使这可能还不够。然后你必须创建一个包装器脚本(对于每个操作系统),它捕获输出并将其写入文件。