Нет входных данных из 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 и посмотрите, появится ли вывод сразу.

Другая причина может заключаться в том, что программа ожидает ввода или закрытия своего стандартного ввода перед тем, как что-либо предпринять, хотя это на самом деле не соответствует описанным выше симптомам.

Другие вопросы по тегам