This is a follow up to this question. The answer suggested there is
to copy the Process out, err, and input streams to the System versions
with IOUtils.copy
as follows (after fixing various compilation errors):
import org.apache.commons.io.IOUtils;
import java.io.IOException;
public class Test {
public static void main(String[] args)
throws IOException, InterruptedException {
final Process process = Runtime.getRuntime().exec("/bin/sh -i");
new Thread(new Runnable() {public void run() {
try {
IOUtils.copy(process.getInputStream(), System.out);
} catch (IOException e) {}
} } ).start();
new Thread(new Runnable() {public void run() {
try {
IOUtils.copy(process.getErrorStream(), System.err);
} catch (IOException e) {}
} } ).start();
new Thread(new Runnable() {public void run() {
try {
IOUtils.copy(System.in, process.getOutputStream());
} catch (IOException e) {}
} } ).start();
process.waitFor();
}
}
However, the resulting code doesn't work for interactive processes like the one executing sh -i
command. In the latter case there is no response to any of the sh
commands.
So my question is: could you suggest an alternative to copy the streams that will work with interactive processes?
The problem is that
IOUtil.copy()
is running while there is data in the InputStream to be copied. Since your process only produces data from time to time,IOUtil.copy()
exits as it thinks there is no data to be copied.Just copy data by hand and use a boolean to stop the thread form outside:
This reads in chunks as many bytes as there are available on inputStream and copies all of them to output. Internally InputStream puts thread so
wait()
and then wakes it when data is available.So it's as efficient as you can have it in this situation.
Process.getOutputStream()
returns aBufferedOutputStream
, so if you want your input to actually get to the subprocess you have to callflush()
after everywrite()
.You can also rewrite your example to do everything on one thread (although it uses polling to read both System.in and the process' stdout at the same time):
You should instead use the
ProcessBuilder.redirectOutput
method & friends. Read more here