Нет входных данных из InputStream ProcessBuilder, пока внешний процесс не будет закрыт
При создании оболочки для консольного приложения я столкнулся с этой странной проблемой, когда входной поток, подключенный к выходу (stdout) внешнего процесса, полностью пуст, пока внешний процесс не завершится.
Мой код, как показано ниже:
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
public class Example{
public static void main(String args[]) throws IOException{
File executable = ...
ProcessBuilder pb = new ProcessBuilder(executable.getCanonicalPath());
pb.redirectErrorStream(true);
Process p = pb.start();
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream(), Charset.forName("UTF-8")));
String line;
while((line = br.readLine()) != null){
System.out.println(line);
}
}
}
Я пробовал несколько вариантов чтения из входного потока, и все они привели к тому же поведению.
Я пробовал:
CharBuffer charBuf = CharBuffer.allocate(1000);
InputStreamReader isr = new InputStreamReader(p.getInputStream(), Charset.forName("UTF-8"));
while(isr.read(charBuf) != -1){
System.out.print(charBuf.flip().toString());
}
а также
byte[] buf = new byte[1000];
int r;
while((r = p.getInputStream().read(buf)) != -1){
System.out.print(new String(buf, 0, r));
}
все безрезультатно.
Где-то вдоль линии выход из внешнего процесса буферизуется (неопределенно), и я не могу понять, где именно. Кажется, что загрузка процесса из командной строки работает нормально, когда я вижу, что вывод выводится мгновенно. Самым странным является тот факт, что тот факт, что завершение внешнего процесса приводит к потоку всех "буферизованных" выходов одновременно (много всего).
К сожалению, у меня нет доступа к источнику внешнего процесса, но, учитывая, что он пишет в stdout нормально, когда в консоли не должно быть никакого значения (насколько я знаю).
Любые идеи приветствуются.
Редактировать:
В одном ответе я рекомендовал переписать программу чтения для потоков вывода и ошибок для запуска в отдельном потоке. Моя фактическая реализация делает это! И все же проблема все еще существует. Приведенный выше код представляет собой SSCCE моего фактического кода, сжатого для удобства чтения, реальный код включает отдельный поток для чтения из InputStream
,
Изменить 2:
Пользователь FoggyDay, кажется, предоставил ответ, который определяет, как изменяется поведение буферизации вывода при выводе между консолью и неконсолями. В то время как процессы, которые обнаруживают, что они записывают в консоль, используют буферизацию строки (буферизируется сбрасываемой каждой новой строкой), запись в неконсоли (все, что он обнаруживает, чтобы не быть консолью) может быть полностью буферизована (размером примерно до 8 КБ).). Если я сделаю спам внешнего процесса (8K от lorem ipsum в цикле for), он действительно появится. Я предполагаю, что мой вопрос сейчас заключается в том, как сделать так, чтобы моя Java-программа запускала буферизацию строки во внешнем процессе.
2 ответа
На ваш вопрос "как сделать так, чтобы моя Java-программа вызывала буферизацию строки во внешнем процессе":
В Linux вы можете использовать программу "stdbuf" (пакет coreutils): stdbuf -oL your_program program_args
Вам нужно только изменить stdout, так как по умолчанию stderr небуферизован. Страница руководства setlinebuf дает дополнительную справочную информацию, если вы заинтересованы: http://linux.die.net/man/3/setlinebuf
Некоторое программное обеспечение проверяет, выполняет ли запись в терминал, и переключает поведение на небуферизованный вывод. Это может быть причиной, почему это работает в терминале. Передайте вывод в cat и посмотрите, появится ли вывод сразу.
Другая причина может заключаться в том, что программа ожидает ввода или закрытия своего стандартного ввода перед тем, как что-либо предпринять, хотя это на самом деле не соответствует описанным выше симптомам.