在 Java 中执行外部命令的经典问题?
好吧 - 这对很多人来说都是一个问题 - 由于我还没有看到有效的答案,我想我应该表达这个问题,以便解决这个问题的人可以告诉我们其他人。
问题是下面三分之二的代码工作得很好。
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您没有显示程序产生的输出,但我猜可能是这样,
ps ax
和ls -l /
不产生输出,但/bin/tar
可以。原因是,前两个命令将输出输出到stdout
而不是stderr
,而后者将在stderr
上生成输出(因为您是未向tar
提供有效参数),但不在stdout
上提供有效参数。这是在 shell 中运行命令时的区别:
使用
>
重定向标准输出,使用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
andls -l /
do not yield output, but/bin/tar
does. The reason is, that the first two commands produce output tostdout
but notstderr
, whereas the latter will produce output onstderr
(since you are not giving valid parameters totar
) but not onstdout
.Here's the difference when running the commands in the shell:
Using
>
to redirect the standard output and2>
to redirect the error output to different files, you can see thattar
produces messages onstderr
and the other two onstdout
(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 oftar
as third process?我在我的系统上运行了您的代码,在正常退出之前,它给了我以下输出:
ps ax
和ls -l /
都没有输出,但是从shell 确认他们没有向标准错误写入任何内容。在我的系统上,您的代码碰巧正常完成,但我可以想象这种情况不会发生的情况。请注意,如果进程在其标准输出上生成大量输出,则缓冲区可能会填满,从而导致进程挂起。我建议使用
ProcessBuilder
而不是Runtime.getRuntime().exec("...")
。一方面,它允许您将标准错误流重定向到标准输出流,然后不必担心从两个流中的哪一个读取。I ran your code on my system and it gave me the following output, before exiting normally:
There was no output from
ps ax
norls -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 ofRuntime.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.令人着迷的(斯波克眉毛) - 从上面的评论来看,看起来有很好的理由解释为什么这么多人对此有问题 - 我们的实现似乎工作方式完全不同!
我正在运行Ubuntu。有趣的是,使用 ProcessBuilder 逆转了问题......但至少现在在使用它时似乎没有一个“永远阻塞”。 - 至少 stderr 和 stdin 能够被读取!
所以对我来说,目前的经验法则似乎是:在 Ubuntu(Oracle / Sun VM)上使用“旧方法”(Runtime.getRuntime().exec())执行命令 shell(“内部”)命令-使用 ProcessBuilder 执行外部命令(如 tar 等):
...
--这对我们许多人来说是一个非常重要的操作...最好能整理出一个关于在哪个平台上使用什么的矩阵......? (即我想知道 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.):
...
--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?)
您可能开始运行 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.