如何使管道与 Runtime.exec()一起工作?

考虑以下代码:

String commandf = "ls /etc | grep release";


try {


// Execute the command and wait for it to complete
Process child = Runtime.getRuntime().exec(commandf);
child.waitFor();


// Print the first 16 bytes of its output
InputStream i = child.getInputStream();
byte[] b = new byte[16];
i.read(b, 0, b.length);
System.out.println(new String(b));


} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}

该程序的输出是:

/etc:
adduser.co

当然,当我从 shell 运行时,它的工作方式和预期的一样:

poundifdef@parker:~/rabbit_test$ ls /etc | grep release
lsb-release

互联网告诉我,由于管道行为不是跨平台的,在 Java 工厂生产 Java 的聪明人不能保证管道工作。

我怎么能这么做?

我不会使用 Java 构造而不是 grepsed来完成所有的解析,因为如果我想改变语言,我将被迫用那种语言重写我的解析代码,这是完全不可能的。

在调用 shell 命令时,如何让 Java 执行管道和重定向?

77273 次浏览

创建一个运行时来运行每个进程。从第一个运行时获取 OutputStream,并从第二个运行时将其复制到 InputStream 中。

编写一个脚本,并执行该脚本而不是单独的命令。

管道是外壳的一部分,所以你也可以这样做:

String[] cmd = {
"/bin/sh",
"-c",
"ls /etc | grep release"
};


Process p = Runtime.getRuntime().exec(cmd);

我在 Linux 中遇到过类似的问题,除了它是“ ps-ef | grep some process”。
至少对于“ ls”你有一个独立于语言的 Java 替代品(尽管比较慢)。例如:

File f = new File("C:\\");
String[] files = f.listFiles(new File("/home/tihamer"));
for (String file : files) {
if (file.matches(.*some.*)) { System.out.println(file); }
}

使用“ ps”则有点困难,因为 Java 似乎没有针对它的 API。

我听说Sigar也许能帮助我们: Https://support.hyperic.com/display/sigar/home

然而,最简单的解决方案(正如 Kaj 所指出的)是以字符串数组的形式执行管道命令。完整代码如下:

try {
String line;
String[] cmd = { "/bin/sh", "-c", "ps -ef | grep export" };
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader in =
new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((line = in.readLine()) != null) {
System.out.println(line);
}
in.close();
} catch (Exception ex) {
ex.printStackTrace();
}

至于为什么 String 数组可以用管道工作,而单个字符串却不能... ... 这是宇宙中的一个谜团(特别是如果你还没有读过源代码的话)。我怀疑这是因为当给 exec 一个字符串时,它首先解析它(以我们不喜欢的方式)。相反,当给 exec 一个字符串数组时,它只是将其传递给操作系统,而不解析它。

实际上,如果我们从繁忙的一天中抽出时间来看看源代码 (at http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/Runtime.java#Runtime.exec%28java.lang.String%2Cjava.lang.String[]%2Cjava.io.File%29), we find that is exactly what is happening:

public Process  [More ...] exec(String command, String[] envp, File dir)
throws IOException {
if (command.length() == 0)
throw new IllegalArgumentException("Empty command");
StringTokenizer st = new StringTokenizer(command);
String[] cmdarray = new String[st.countTokens()];
for (int i = 0; st.hasMoreTokens(); i++)
cmdarray[i] = st.nextToken();
return exec(cmdarray, envp, dir);
}

@ Kaj 接受的答案是 linux,这是 Windows 的等价答案:

String[] cmd = {
"cmd",
"/C",
"dir /B | findstr /R /C:"release""
};
Process p = Runtime.getRuntime().exec(cmd);