Start external executable from Java code with streams redirection
I need to start external executable in such way that user can interact with program that was just started.
For example in OpenSuse Linux there is a package manager - Zypper. You can start zypper in command mode and give commands like install, update, remove, etc. to it.
I would like to run it from Java code in a way user could interact with it: input c开发者_运维技巧ommands and see output and errors of the program he started.
Here is a Java code I tried to use:
public static void main(String[] args) throws IOException, InterruptedException {
Process proc = java.lang.Runtime.getRuntime().exec("zypper shell");
InputStream stderr = proc.getInputStream();
InputStreamReader isr = new InputStreamReader(stderr);
BufferedReader br = new BufferedReader(isr);
String line = null;
char ch;
while ( (ch = (char)br.read()) != -1)
System.out.print(ch);
int exitVal = proc.waitFor();
System.out.println("Process exitValue: " + exitVal);
}
But unfortunately I can only see it's output:
zypper>
but no matter what I write, my input doesn't affect program that was started. How can I do what want to?
You need to get an output stream in order to write to the process:
OutputStream out = proc.getOuptutStream();
This output stream is piped into the standard input stream of the process, so you can just write to it (perhaps you want to wrap it in a PrintWriter
first) and the data will be sent to the process' stdin.
Note that it might also be convenient to get the error stream (proc.getErrorStream
) in order to read any error output that the process writes to its stderr.
API reference:
- http://download.oracle.com/javase/6/docs/api/java/lang/Process.html
Seems like the converting inside the while condition fails in your example, this seems to work better (I don't run Suse so I haven't tried with Zypper):
public static void main(String[] args) throws IOException, InterruptedException
{
//Process proc = java.lang.Runtime.getRuntime().exec("zypper shell");
Process proc = java.lang.Runtime.getRuntime().exec("ping -t localhost");
InputStream stderr = proc.getInputStream();
InputStreamReader isr = new InputStreamReader(stderr);
BufferedReader br = new BufferedReader(isr);
int i;
while ( (i = br.read()) != -1)
{
System.out.print((char) i);
}
int exitVal = proc.waitFor();
System.out.println("Process exitValue: " + exitVal);
}
I recently wrapped Google Closure Compiler into a .jar-file which is extracted and used in a Process. This compiler only talks via System.in/out/err. There's a big "gotcha" in connecting pipes together, which is just briefly mentioned in the Process javadoc.
"...failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, and even deadlock."
On Mac OS X the buffer is 16k, and if you don't read it promptly as suggested, the process deadlocks. My only solution to this problem ATM, is a rather nasty busy wait.
https://github.com/algesten/googccwrap/blob/master/src/main/java/googccwrap/GoogleClosureCompilerWrapper.java
精彩评论