为什么我的Java代码错误地执行bash命令?

Jay*_*man 3 java linux bash pipe

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

echo 5 | ./prog
Run Code Online (Sandbox Code Playgroud)

从bash本身打印正确的答案25,stdout但运行

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());
    }
}
Run Code Online (Sandbox Code Playgroud)

意外地给出了5 | ./prog.解决办法是什么?

Ste*_*n C 12

Java exec无法像这样运行shell命令.如果要运行shell命令,则需要显式调用shell; 例如

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

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

如果您想了解Java如何处理这个问题的更多细节,那么有源代码......

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

解决方案的解释:

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

  • 使用"sh"的原因是......嗯......你需要一个shell来解析和处理shell命令行.Java的exec命令不支持shell语法......正如javadoc所解释的那样.

  • "-c"选项的目的由"man sh"解释.基本上,sh -c "a b c"意味着"使用'sh'来运行命令行'ab c'".

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