2014-01-08 4 views
3

При создании оболочки для консольного приложения я столкнулся с этой странной проблемой, когда входной поток, подключенный к выходу (стандартный вывод) внешнего процесса, полностью пуст до внешний выходы процесса.Нет ввода InputStream ProcessBuilder до закрытия внешнего процесса

Мой код, как показано ниже:

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, когда в консоли не должно быть никакого изменения там (насколько я знаю).

Любые идеи приветствуются.

Edit:

Один ответ рекомендовал мне переписать читатель для вывода и ошибок потоков для запуска в отдельном потоке. Моя фактическая реализация делает это! И все же проблема все еще существует. Код, указанный выше, представляет собой SSCCE моего фактического кода, сконденсированного для удобства чтения, фактический код включает отдельный поток для чтения из InputStream.

Edit 2:

Пользователь FoggyDay, кажется, дал ответ, который определяет, как поведение изменения буферизации вывода при выводе между консолью и не-консолей. В то время как процессы, которые обнаруживают, что они пишут на консоль, используют буферизацию строк (буферизованная очистка каждой новой строки), запись в неконсоли (все, что она обнаруживает, чтобы не быть консолью) может быть полностью буферизована (до размера примерно 8K). Если я делаю спам внешнего процесса (8K lorem ipsum в цикле for), выход действительно появляется. Думаю, теперь мой вопрос заключается в том, как заставить мою java-программу запускать буферизацию по внешнему процессу.

+1

Зачем использовать BufferedReader, если вы не хотите буферизации? Вместо этого я могу переключиться на «java.util.Scanner». –

+0

@ElliottFrisch Первоначально я хотел получить некоторую форму буферизации, но, учитывая, что это не совсем сработало, я начал использовать «CharBuffer» и сортировать, чтобы попробовать устранить проблему. моя последняя попытка заключалась в том, что я читал исходный поток InputStream напрямую и все же, выход не отображается. Учитывая, что Scanner построен поверх исходного InputStream, я не вижу, как он будет работать, но я все равно попробую. – initramfs

+0

Я предполагаю, что вы уже пробовали свой код с другими исполняемыми файлами и видели, что его выход доступен до завершения процесса? Любой случай, когда этот исполняемый файл делает что-то напуганное, например, проверяет, перенаправляется ли выход? –

ответ

-1

Некоторое программное обеспечение проверяет, записывает ли он терминал и переключает поведение на небуферизованный вывод. Это может быть причиной, почему она работает в терминале. Выведите выходной сигнал в cat и посмотрите, будет ли результат все же отображаться немедленно.

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

1

На ваш вопрос «как сделать мой Java программы буферизации запуска линии на внешний процесс»:

В Linux вы можете использовать «stdbuf» программа (Coreutils пакет): stdbuf-ола your_program program_args

Вам нужно только изменить stdout, так как stderr не загружается по умолчанию. Страница man setlinebuf дает дополнительную справочную информацию, если вам интересно: http://linux.die.net/man/3/setlinebuf

+0

Мое приложение предназначено для кросс-платформенной, поэтому решение, специфичное для Linux, недостаточно. Я посмотрю источник для stdbuf, хотя, чтобы увидеть, есть ли что-нибудь, что я могу с ним сделать. – initramfs

+0

Пробовал это в окнах (git поставляется с stdbuf.exe), и, похоже, он не работает, я думаю, JDK буферизуется? – teknopaul

Смежные вопросы