问题
I am trying to make my Java program interact with Linux bash but something goes wrong. I have a simple executable prog
that reads the one integer from stdin
and outputs its square. Executing
echo 5 | ./prog
from bash itself prints correct answer 25
in stdout
but running
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?
回答1:
Java exec
cannot run shell commands like that. If you want to run a shell command, you need to explicitly invoke the shell; e.g.
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.
If you want even more detail on how Java handles this, there is the source code ...
If you want to understand in depth why Java doesn't handle 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 different shells, 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.
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 arguments correctly. It can't. This is an example where there are two commands in a pipeline.The reason for using "sh" is ... well ... you need a shell to parse and process a shell command line. Java's
exec
command 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'".
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.
来源:https://stackoverflow.com/questions/18424541/why-does-my-java-code-execute-bash-command-incorrectly