简体   繁体   English

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

[英]How to make pipes work with Runtime.exec()?

Consider the following code:考虑以下代码:

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

The program's output is:程序的输出是:

/etc:
adduser.co

When I run from the shell, of course, it works as expected:当然,当我从 shell 运行时,它按预期工作:

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

The internets tell me that, due to the fact that pipe behavior isn't cross-platform, the brilliant minds who work in the Java factory producing Java can't guarantee that pipes work.互联网告诉我,由于管道行为不是跨平台的,在生产 Java 的 Java 工厂工作的聪明人不能保证管道工作。

How can I do this?我怎样才能做到这一点?

I am not going to do all of my parsing using Java constructs rather than grep and sed , because if I want to change the language, I'll be forced to re-write my parsing code in that language, which is totally a no-go.我不打算使用 Java 结构而不是grepsed来完成所有的解析,因为如果我想更改语言,我将被迫用该语言重新编写我的解析代码,这完全是不可能的 -走。

How can I make Java do piping and redirection when calling shell commands?调用shell命令时如何让Java进行管道和重定向?

Write a script, and execute the script instead of separate commands.编写脚本,然后执行脚本而不是单独的命令。

Pipe is a part of the shell, so you can also do something like this:管道是外壳的一部分,因此您也可以执行以下操作:

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

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

I ran into a similar problem in Linux, except it was "ps -ef | grep someprocess".我在 Linux 中遇到了类似的问题,除了它是“ps -ef | grep someprocess”。
At least with "ls" you have a language-independent (albeit slower) Java replacement.至少使用“ls”,你有一个独立于语言(虽然速度较慢)的 Java 替代品。 Eg.:例如。:

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

With "ps", it's a bit harder, because Java doesn't seem to have an API for it.使用“ps”有点困难,因为 Java 似乎没有它的 API。

I've heard that Sigar might be able to help us: https://support.hyperic.com/display/SIGAR/Home我听说 Sigar 可能会帮助我们: https : //support.hyperic.com/display/SIGAR/Home

The simplest solution, however, (as pointed out by Kaj) is to execute the piped command as a string array.然而,最简单的解决方案(如 Kaj 所指出的)是将管道命令作为字符串数组执行。 Here is the full code:这是完整的代码:

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();
}

As to why the String array works with pipe, while a single string does not... it's one of the mysteries of the universe (especially if you haven't read the source code).至于为什么 String 数组可以与管道一起使用,而单个字符串则不能……这是宇宙的奥秘之一(尤其是如果您还没有阅读源代码的话)。 I suspect that it's because when exec is given a single string, it parses it first (in a way that we don't like).我怀疑这是因为当给 exec 一个字符串时,它首先解析它(以我们不喜欢的方式)。 In contrast, when exec is given a string array, it simply passes it on to the operating system without parsing it.相反,当给 exec 一个字符串数组时,它只是将它传递给操作系统而不对其进行解析。

Actually, if we take time out of busy day and look at the source code (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:实际上,如果我们在忙碌的一天中抽出时间查看源代码(在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 ),我们发现这正是正在发生的事情:

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

Create a Runtime to run each of the process.创建一个运行时来运行每个进程。 Get the OutputStream from the first Runtime and copy it into the InputStream from the second one.从第一个 Runtime 获取 OutputStream 并将其从第二个 Runtime 复制到 InputStream。

@Kaj accepted answer is for linux. @Kaj 接受的答案适用于 linux。 This is the equivalent one for Windows:这是 Windows 的等效项:

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

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

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