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

发布于 2024-10-20 05:21:23 字数 2094 浏览 2 评论 0原文

好吧 - 这对很多人来说都是一个问题 - 由于我还没有看到有效的答案,我想我应该表达这个问题,以便解决这个问题的人可以告诉我们其他人。

问题是下面三分之二的代码工作得很好。

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

}

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 cannot read the result of the successful launch of an external file. Attempting to do any type of read (realine, etc.) on either the stdin or stderr InputStream blocks forever:

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

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

别念他 2024-10-27 05:21:23

您没有显示程序产生的输出,但我猜可能是这样, ps axls -l / 不产生输出,但 /bin/tar 可以。原因是,前两个命令将输出输出到 stdout 而不是 stderr,而后者将在 stderr 上生成输出(因为您是未向 tar 提供有效参数),但不在 stdout 上提供有效参数。

这是在 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]$ 

使用 > 重定向标准输出,使用 2> 将错误输出重定向到不同的文件,您可以看到 < code>tar 在 stderr 上生成消息,在 stdout 上生成另外两个消息(其他文件的文件大小为零,没有输出)。

可能是这样吗?如果您运行例如 echo "Foo" 而不是 tar 作为第三个进程,会发生什么?

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. 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.

Here's the difference when running the commands in the 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).

May this be the case? What happens if you run, e. g. echo "Foo" instead of tar as third process?

亢潮 2024-10-27 05:21:23

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

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("...")。一方面,它允许您将标准错误流重定向到标准输出流,然后不必担心从两个流中的哪一个读取。

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. 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("..."). 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.

时光倒影 2024-10-27 05:21:23

令人着迷的(斯波克眉毛) - 从上面的评论来看,看起来有很好的理由解释为什么这么多人对此有问题 - 我们的实现似乎工作方式完全不同!

我正在运行Ubuntu。有趣的是,使用 ProcessBuilder 逆转了问题......但至少现在在使用它时似乎没有一个“永远阻塞”。 - 至少 stderr 和 stdin 能够被读取!

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

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

...

reader3: /bin/tar: 您必须指定
-Acdtrux 选项 reader3 之一:
尝试
/bin/tar --help' 或 `/bin/tar
--usage' 了解更多信息。

--这对我们许多人来说是一个非常重要的操作...最好能整理出一个关于在哪个平台上使用什么的矩阵......? (即我想知道 OpenJDK 在 Ubuntu 上是否会做得更好?)

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. Interestingly, using ProcessBuilder inverted the problem... but at least none now seem to "block forever" when using it. -At least stderr and stdin were able to be read!

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.):

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.

--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 ....? (i.e. I wonder if OpenJDK would do better on Ubuntu?)

悲喜皆因你 2024-10-27 05:21:23

您可能开始运行 RunningReaders 的时间太晚了。
您启动三个进程,在您开始读取 RunningReader 中的 Error-OutputStream 之前,第一个进程有时可能已经完成。

RunningReaders 必须在进程启动后立即启动。

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

You are probably starting the RunningReaders to late.
You start three processes, and probably the first one will sometimes have finished before you start reading the Error-OutputStream in your RunningReader.

The RunningReaders must be started immediately after the process has started.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文