Как надежно читать и писать в работающий внешний процесс из Java?
Я хотел бы иметь возможность порождать внешний процесс из Java, периодически писать на его вход и читать ответ, как если бы это была консоль. Однако большую часть времени, когда я читаю вывод процесса, ничего не доступно. Есть ли хорошая практика, чтобы делать такие вещи (даже избегать этого)?
Вот урезанный пример того, что не работает:
import org.apache.commons.exec.*;
import java.io.*;
//...
CommandLine cl = CommandLine.parse("/usr/bin/awk {print($1-1)}");
System.out.println(cl.toStrings());
Process proc = new ProcessBuilder(cl.toStrings()).start();
OutputStream os = proc.getOutputStream(); // avoiding *Buffered* classes
InputStream is = proc.getInputStream(); // to lessen buffering complications
os.write(("4" + System.getProperty("line.separator")).getBytes());
os.flush(); // Doesn't seem to flush.
// os.close(); // uncommenting works, but I'd like to keep the process running
System.out.println("reading");
System.out.println(is.read()); // read even one byte? usually is.available() -> 0
Странно, если я оберну OutputStream в BufferedWriter, я смогу читать из некоторых процессов (cat), но не из других (awk, grep).
1 ответ
Как правило, принятый подход является правильным. Несколько вещей, хотя:
InputStream.read()
это метод блокировки. Он ожидает ввода и не загружает процессор. Вы должны вращаться вокруг этого...- Можно прочитать более одного байта, просто используйте
read(byte[] buffer, int offset, int len)
- Обтекание входных потоков в BufferedInputStream облегчает доступ (
readLine()
метод). Это альтернатива BufferedReader. - Не забудьте использовать
Process.waitFor()
,
Также убедитесь, что внешний процесс записывает в стандартный вывод (а не в стандартную ошибку). Две возможности здесь:
- использование
Process.getErrorStream()
(и относиться к нему как к другому InputStream) - измените командную строку на
/usr/bin/awk {print($1-1)} 2>&1
, Это перенаправит стандартную ошибку на стандартный вывод.