简体   繁体   English

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

[英]Classic problem with executing external commands in Java?

Okay - This one is a bit of a problem for a lot of folks - Since I have yet to see an answer that works, I thought I would express the problem so someone who has figured it out can tell the rest of us. 好的-对于很多人来说,这是一个问题-由于我尚未看到有效的答案,我想我会表达这个问题,以便有人找出来可以告诉我们其他人。

The problem is that two out of three of the below work just fine -same code. 问题是,以下三分之二的代码都可以正常工作。

The instance for reader3 demonstrates the problem. reader3的实例演示了该问题。 Reader3 cannot read the result of the successful launch of an external file. Reader3无法读取成功启动外部文件的结果。 Attempting to do any type of read (realine, etc.) on either the stdin or stderr InputStream blocks forever: 尝试永久性地在stdin或stderr InputStream块上进行任何类型的读取(读取等):

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.");
}

} }

You did not show the output your program yields, but I guess it might be the case, that ps ax and ls -l / do not yield output, but /bin/tar does. 您没有显示程序产生的输出,但是我想可能是这样, ps axls -l /不产生输出,但是/bin/tar可以。 The reason is, that the first two commands produce output to stdout but not stderr , whereas the latter will produce output on stderr (since you are not giving valid parameters to tar ) but not on stdout . 原因是,前两个命令将输出输出到stdout而不是stderr ,而后者将在stderrstderr (因为您没有为tar提供有效的参数),而在stdout上却没有stdout

Here's the difference when running the commands in the shell: 这是在Shell中运行命令时的区别:

[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]$ 

Using > to redirect the standard output and 2> to redirect the error output to different files, you can see that tar produces messages on stderr and the other two on stdout (file sizes for the other files are zero, there was no output). 使用>重定向标准输出,并使用2>将错误输出重定向到其他文件,您可以看到tarstderr上生成消息,而在stdout上生成其他两个消息(其他文件的文件大小为零,没有输出)。

May this be the case? 可能是这样吗? What happens if you run, eg echo "Foo" instead of tar as third process? 如果您运行,例如echo "Foo"而不是tar作为第三过程,会发生什么?

I ran your code on my system and it gave me the following output, before exiting normally: 我在系统上运行了您的代码,在正常退出之前,它提供了以下输出:

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.

There was no output from ps ax nor ls -l / , but running them from the shell confirms that they didn't write anything to standard error. ps axls -l /都没有输出,但是从shell运行它们可以确认它们没有向标准错误写入任何内容。 On my system, your code happened to complete normally, but I can imagine situations where this doesn't happen. 在我的系统上,您的代码碰巧正常完成,但是我可以想象没有发生这种情况。 Do be aware that if a process generates a lot of output on its standard output, a buffer could fill up and that would cause the process to hang. 请注意,如果一个进程在其标准输出上生成大量输出,则缓冲区可能会填满,这将导致该进程挂起。

I'd recommend using a ProcessBuilder instead of Runtime.getRuntime().exec("...") . 我建议使用ProcessBuilder而不是Runtime.getRuntime().exec("...") For one thing, it allows you to redirect the standard error stream into the standard output stream and then not have to worry about which of the two streams to read from. 一方面,它使您可以将标准错误流重定向到标准输出流,而不必担心要从这两个流中读取哪个。

Facinating (spockian eyebrow) - judging from the above comments, it looks as there are good reasons why so many folks have problems with this - our implementations each seem to work quite differently! 令人着迷(斯巴克式的眉毛)-从以上评论来看,看来有很多原因使很多人对此有疑问-我们的实现似乎各有不同!

I am running Ubuntu. 我正在运行Ubuntu。 Interestingly, using ProcessBuilder inverted the problem... but at least none now seem to "block forever" when using it. 有趣的是,使用ProcessBuilder可以解决问题……但是现在至少没有一个似乎永远“阻塞”。 -At least stderr and stdin were able to be read! -至少能够读取stderr和stdin!

So for me, for now, the rule of thumb seems to be: Use the "old way" (Runtime.getRuntime().exec()) for command-shell ('internal') commands on Ubuntu (Oracle / Sun VM) -Use ProcessBuilder for external commands (like tar, etc.): 因此,对我而言,目前的经验法则似乎是:在Ubuntu(Oracle / Sun VM)上对Command-Shell(“内部”)命令使用“旧方法”(Runtime.getRuntime()。exec()) -将ProcessBuilder用于外部命令(例如tar等):

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

... ...

reader3: /bin/tar: You must specify one of the -Acdtrux' options reader3: Try /bin/tar --help' or `/bin/tar --usage' for more information. reader3:/ bin / tar:您必须指定-Acdtrux' options reader3: Try有关更多信息, -Acdtrux' options reader3: Try / bin / tar --help'或`/ bin / tar --usage'。

--This is a pretty important operation for many of us ... It would be nice to put together a matrix on what-to-use on which-platform ....? -这对我们许多人来说是一项非常重要的操作...将矩阵汇总到在哪种平台上使用什么....? (ie I wonder if OpenJDK would do better on Ubuntu?) (即我想知道OpenJDK是否可以在Ubuntu上做得更好?)

You are probably starting the RunningReaders to late. 您可能正在启动RunningReaders到很晚。 You start three processes, and probably the first one will sometimes have finished before you start reading the Error-OutputStream in your RunningReader. 您开始三个过程,可能第一个过程有时会在您开始在RunningReader中读取Error-OutputStream之前完成。

The RunningReaders must be started immediately after the process has started. 该过程开始后,必须立即启动RunningReaders。

Under certain conditions even that might not be sufficient. 在某些情况下,这可能还不够。 Then you would have to create a wrapper script (for each OS) that captures the output and writes it to a file. 然后,您将不得不针对每个操作系统创建一个包装器脚本,以捕获输出并将其写入文件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM