Не удалось прочитать InputStream из Java Process (Runtime.getRuntime(). Exec() или ProcessBuilder)
Я пытаюсь запустить процесс извне с Java и не могу читать ничего из его InputStream.
Если я начинаю процесс с такими командами, как "ls", "ps" или "kill", все работает нормально. Я могу запустить процесс и получить информацию либо на InputStream, либо на ErrorStream процесса.
Если я пытаюсь использовать команду "ftp" или "telnet", как InputStream, так и ErrorStream блокируют мою программу при попытке прочитать. Никакая информация не передается через эти потоки в любое время.
Может ли кто-нибудь объяснить поведение? Это просто невозможно с этими командами или у меня есть проблема с моей реализацией?
String processName = _configuration.getProgramCommand().getCommand();
ProcessBuilder procBuilder = new ProcessBuilder(processName);
System.out.println("Starting process "+processName);
_proc = Runtime.getRuntime().exec(processName);// procBuilder.start();
if(!procBuilder.redirectErrorStream()) {
_errorWorker = new ProcessErrorWorker(_proc);
_errorWorker.start();
}
String proc_start_answer = _configuration.getNextCommand().getCommand();
System.out.println("Waiting for process answer '"+proc_start_answer+"'");
BufferedReader input = new BufferedReader(new InputStreamReader(_proc.getInputStream()));
String answer = "";
try {
System.out.println("inputstream ready: "+input.ready());
answer+=input.readLine();
System.out.println("process answer: "+answer);
input.close();
} catch(Exception e) {
System.out.print(e.getMessage());
}
Ответы
Ответ 1
Я понимаю, что это вопрос старый, но я просто испытал ту же проблему. Чтобы исправить это, я использовал этот код.
List<String> commandAndParameters = ...;
File dir = ...; // CWD for process
ProcessBuilder builder = new ProcessBuilder();
builder.redirectErrorStream(true); // This is the important part
builder.command(commandAndParameters);
builder.directory(dir);
Process process = builder.start();
InputStream is = process.getInputStream();
Похоже, что процесс ожидает, что вы также прочитаете из потока ошибок. Лучшее решение для этого - объединить поток ввода и ошибки вместе.
Обновление
Я также не видел, чтобы вы попытались прочитать из потока ошибок. Возможно, вам нужно объединить их вручную с помощью redirectErrorStream(true)
Ответ 2
Вам нужно выполнить эту работу в потоке. Например, для регистрации стандартного вывода:
Process process = Runtime.getRuntime().exec(command);
LogStreamReader lsr = new LogStreamReader(process.getInputStream());
Thread thread = new Thread(lsr, "LogStreamReader");
thread.start();
public class LogStreamReader implements Runnable {
private BufferedReader reader;
public LogStreamReader(InputStream is) {
this.reader = new BufferedReader(new InputStreamReader(is));
}
public void run() {
try {
String line = reader.readLine();
while (line != null) {
System.out.println(line);
line = reader.readLine();
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Затем вам понадобится второй поток для обработки ввода. И вы можете иметь дело с stderr так же, как stdout.
Ответ 3
У меня была проблема с печатью на C-программе в stdout...
Решение redirectErrorStream(true)
с fflush(stdout)
в коде C сделало трюк для меня.
Ответ 4
После боев в течение нескольких дней и пробовав множество опций , я нашел решение. Работа над Ubuntu 14.04 x64, с jdk 8 u 25. Этот комментарий кодового просмотра дал мне подсказку.
Фокус в том, чтобы использовать InputStream#available()
перед чтением чего-либо с помощью BufferedReader
. У меня лично есть два потока: один для stdout и другой для stderr. Вот простой исполняемый фрагмент, который я экстраполировал из своей программы. Если вы не хотите читать все, просто перейдите к readLine()
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.InputStream;
import java.io.BufferedReader;
import java.nio.charset.Charset;
public class LogHandler {
private BufferedReader bufferedReader;
private InputStream inputStream;
private Thread thread;
private boolean isRunning;
public void open (InputStream inputStream) {
this.inputStream = inputStream;
this.bufferedReader = new BufferedReader(new InputStreamReader(inputStream, Charset.defaultCharset()));
isRunning = true ;
if(thread!=null){
thread = new Thread (()->{
while(isRunning) {
String line = null;
try {
line = readLine();
} catch (IOException e) {
isRunning = false;
}
if(line == null) {
try {
Thread.sleep(150);
} catch (InterruptedException ie) {
isRunning=false;
Thread.currentThread().interrupt();
}
} else {
System.out.println(line);
}
}
});
} else throw new IllegalStateException("The logger is already running");
thread.start();
}
public void close() throws InterruptedException {
if(thread!=null){
thread.interrupt();
thread.join();
thread = null;
IOUtils.closeQuietly(inputStream);
} else throw new IllegalStateException("The logger is not running"); }
private String readLine() throws IOException {
if(inputStream.available()>0) { // <--------- THIS IS IT
int kar = bufferedReader.read();
if (kar != -1) {
StringBuilder buffer = new StringBuilder(30);
while (kar != -1 && kar != (int) '\n') {
buffer.append((char) kar);
kar = bufferedReader.read();
}
return buffer.toString();
}
}
return null;
}
}
Ответ 5
У меня была такая же проблема с ftp, пока я не заметил, что ftp обнаруживает, вызван ли он с терминала или нет.
Когда ftp не вызывается из терминала, он приостанавливает вывод на stdout, если не указана опция verbose (-v).
Причина блокировки потоков заключается в том, что им ничего не написано.
Ответ 6
У меня была такая же проблема. К сожалению
processBuilder.redirectErrorStream(true);
не работал у меня; это дало мне представление о том, что не так. Попробуйте использовать следующую строку кода для отображения содержимого stderr:
BufferedReader err=
new BufferedReader(new InputStreamReader(process.getErrorStream()));
Это помогло мне разобраться, что было не так с моими командами терминала, проходящими через каждый поток.