为什么我的 Java 代码执行 bash 命令不正确?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18424541/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 06:23:44  来源:igfitidea点击:

Why does my Java code execute bash command incorrectly?

javalinuxbashpipe

提问by Jay Foreman

I am trying to make my Java program interact with Linux bash but something goes wrong. I have a simple executable progthat reads the one integer from stdinand outputs its square. Executing

我试图让我的 Java 程序与 Linux bash 交互,但出了点问题。我有一个简单的可执行文件prog,可以从中读取一个整数stdin并输出它的平方。执行

echo 5 | ./prog

from bash itself prints correct answer 25in stdoutbut running

从庆典本身打印正确答案25stdout,但运行

import java.io.*;

public class Main {
    public static void main(String[] args) throws InterruptedException, IOException {
        Runtime run = Runtime.getRuntime();
        Process proc = run.exec("echo 5 | ./prog");
        proc.waitFor();
        BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
        while(br.ready())
            System.out.println(br.readLine());
    }
}

unexpectedly gives 5 | ./prog. What is the solution?

居然给了5 | ./prog。解决办法是什么?

回答by Stephen C

Java execcannot run shell commands like that. If you want to run a shell command, you need to explicitly invoke the shell; e.g.

Javaexec不能像这样运行 shell 命令。如果要运行shell命令,则需要显式调用shell;例如

Process proc = run.exec(new String[]{"/bin/sh", "-c", "echo 5 | ./prog"});

For more details on what Java does with this, read the javadocs for exec(String)and exec(String[]). Note that these are "convenience methods", and you need to follow the chain of links to the underlying methods for a complete understanding of what the javadoc is saying.

对于什么用的Java这样做的详细信息,请阅读的javadocexec(String)exec(String[])。请注意,这些是“便捷方法”,您需要遵循指向底层方法的链接链,以完全理解 javadoc 所说的内容。

If you want even more detail on how Java handles this, there is the source code ...

如果您想了解有关 Java 如何处理此问题的更多详细信息,请参阅源代码...

If you want to understand in depth why Java doesn'thandle the shell syntax itself, you probably need to take a deep dive into the architecture and philosophy of UNIX / Linux systems, and the separation of concerns between application, operating system and command shell. Note that there are a myriad differentshells, each with (potentially) different rules for quoting, argument splitting, redirection, pipes, etcetera. Most of the popular shells are similar, but that's just the way things panned out.

如果您想深入了解 Java处理 shell 语法本身的原因,您可能需要深入了解 UNIX/Linux 系统的体系结构和哲学,以及应用程序、操作系统和命令 shell 之间的关注点分离. 请注意,有无数不同的shell,每个 shell 都有(可能)不同的引用、参数拆分、重定向、管道等规则。大多数流行的外壳都是相似的,但这只是事情的发展方式。

Explanation of the solution:

解决方案说明:

  • The reason for splitting the command by hand is that exec(String)won't split the command into a single command and argumentscorrectly. It can't. This is an example where there are twocommands in a pipeline.

  • The reason for using "sh" is ... well ... you needa shell to parse and process a shell command line. Java's execcommand does not support shell syntax ... as the javadoc explains.

  • The purpose of the "-c" option is explained by "man sh". Basically, sh -c "a b c"means "use 'sh' to run the command line 'a b c'".

  • 手动拆分命令的原因是exec(String)不会将命令正确拆分为单个命令和参数。它不能。这是一个管道中有两个命令的示例。

  • 使用“sh”的原因是......好吧......你需要一个shell来解析和处理一个shell命令行。Java 的exec命令不支持 shell 语法……正如 javadoc 所解释的。

  • “-c”选项的用途由“man sh”解释。基本上,sh -c "a b c"意思是“使用‘sh’运行命令行‘ab c’”。

FWIW, it is technically possible to construct and run a pipeline solely in Java (i.e. without relying on an external shell), but it is generally not worth the effort. You've already introduced a platform dependency by running external commands, so an extra dependency in the form of a specific shell command and syntax doesn't make things significantly worse.

FWIW,技术上可以仅用 Java 构建和运行管道(即不依赖外部 shell),但通常不值得付出努力。您已经通过运行外部命令引入了平台依赖项,因此特定 shell 命令和语法形式的额外依赖项不会使事情变得更糟。