Runtime.getRuntime.exec () Execution of Linux scripts causes the program to die
Problem:
In a Java program, executing a Linux script with Runtime.getruntime (). EXEC () causes the program to be suspended, and the script executes directly on the terminal without any problems.
Reason:
First look at the Java code:
Public final static void Process1 (string[] cmdarray) {
Process p = null;
BufferedReader br = null;
try {
p = runtime.getruntime (). exec (Cmdarray);
br = new BufferedReader (New InputStreamReader (P.getinputstream ()));
String line = null;
while (line = Br.readline ()) = null) {
System.out.println (line);
}
P.waitfor ();
} catch (Exception e) {
E.printstacktrace ();
} finally {
if (br! = null) {
try {
Br.close ();
} catch (IOException e) {
E.printstacktrace ();
}
}
if (P! = null) {
P.destroy ();
}
}
}
The script content is simple, the main content is to extract a specified tar.gz file into the specified directory.
After the program is suspended, look at the list of processes and discover a few suspicious points:
[Email protected]:~/work/tgz$ps UX | grep dowjones
Neil 2079 0.0 0.0 2435492-s001 r+ 10:56 am 0:00.00 egrep dowjones
Neil 2077 0.0 0.0 2435080 652?? S 10:56 0:00.24 Tar xvf dowjones.tar.gz
Neil 2073 0.0 0.0 2435488 792?? S 10:56 am 0:00.00/bin/bash/users/neil/bin/genova/genova_crm.sh/users/neil/work/tgz/dowjones.tar.gz/users/neil/wo Rk/dest/dowj
Where genova_crm.sh is the script to execute, the tar xvf dowjones.tar.gz is the command to perform the decompression.
As you can see, the program is stuck on the tar command, and this command is suspended, very strange things ...
Review the JDK documentation again, and discover that the process document says that the standard buffer size is limited and that improper operation of the input and output stream may cause the program to hang.
When you execute the TAR xvf dowjones.tar.gz command Separately, you find that there are more than n outputs, and when you execute through Java, you do not see those outputs.
The Java program only obtains the standard output stream and does not get the error output stream, then it is possible that the error output buffer is full and the tar command hangs.
Workaround:
Modify the Java program, both the standard output stream and the error output stream are processed to ensure that the output buffer is not blocked. The practice is to use an asynchronous thread to read the standard output, read and drop, let the main thread read the error output stream:
Public final static void Process1 (string[] cmdarray) {
try {
Final Process p = runtime.getruntime (). exec (Cmdarray);
New Thread (New Runnable () {
@Override
Publicvoid Run () {
BufferedReader br = new BufferedReader (
New InputStreamReader (P.getinputstream ()));
try {
while (Br.readline ()! = null)
;
Br.close ();
} catch (IOException e) {
E.printstacktrace ();
}
}
}). Start ();
BufferedReader br = null;
br = new BufferedReader (New InputStreamReader (P.geterrorstream ()));
String line = null;
while (line = Br.readline ()) = null) {
System.out.println (line);
}
P.waitfor ();
Br.close ();
P.destroy ();
} catch (Exception e) {
E.printstacktrace ();
}
}
Re-execution, the discovery program can be executed properly, the tar command echo is printed out. Problem solving.
This may be related to a specific tar package, when performing the tar decompression, it is obvious that the echo string is a bit garbled, echo is all output to the error stream.
The above method can avoid the standard output or error output buffer full to hang the main program problem, but need to handle two streams at the same time, there is a duplication of suspicion. If you can output the standard output and the error to a single stream, you only need to process one stream. Processbuilder provides this capability.
There are two ways to create a process, one of which is the runtime.exec of the above, and one that can be processbuilder.start () to produce a process instance.
Processbuilder can set the necessary parameter data, such as command, environment variables, working directory, redirect error flow to standard output, and then start () based on these parameters to generate a process instance, start a child process to execute the corresponding command.
The code is as follows:
Public final static void process (string[] cmdarray) throws Throwable {
Processbuilder PB = new Processbuilder (Cmdarray);
Pb.redirecterrorstream (TRUE);
Process p = null;
BufferedReader br = null;
try {
p = Pb.start ();
br = new BufferedReader (New InputStreamReader (P.getinputstream ()));
String line = null;
Logger.info ("Invoke Shell: {}", Stringutils.join (Cmdarray, ""));
while (line = Br.readline ()) = null) {
Logger.info (line);
}
P.waitfor ();
} finally {
if (br! = null) {
Br.close ();
}
if (P! = null) {
P.destroy ();
}
}
}
Public final static void process (string[] cmdarray) throws Throwable {
Processbuilder PB = new Processbuilder (Cmdarray);
Pb.redirecterrorstream (TRUE);
Process p = null;
BufferedReader br = null;
try {
p = Pb.start ();
br = new BufferedReader (New InputStreamReader (P.getinputstream ()));
String line = null;
Logger.info ("Invoke Shell: {}", Stringutils.join (Cmdarray, ""));
while (line = Br.readline ()) = null) {
Logger.info (line);
}
P.waitfor ();
} finally {
if (br! = null) {
Br.close ();
}
if (P! = null) {
P.destroy ();
}
}
}
As you can see from the code above, the error stream is redirected to the standard output stream, so the program only needs to process the standard output.
Instead, use Runtime.getruntime (). EXEC () to call an external program to get the "standard output stream", which is always blocked. Found on the Internet, think it should be "error output stream" problem. Therefore, for the "error output stream" to open a single thread read, "standard output stream" is no longer blocked. The resolution process is as follows:
http://blog.csdn.net/andylao62/article/details/44564937
Runtime.getRuntime.exec () execution of a Linux script causes the program to die for a problem