Java: невозможно получить данные stdout от Process, если они не были сброшены вручную - PullRequest
2 голосов
/ 20 ноября 2010

Я пишу оболочку терминала для программы командной строки на Java и порождаю подпроцесс с помощью ProcessBuilder.Чтобы отправить нажатия клавиш в подпроцесс, я просто пишу e.getKeyChar() из GUI прямо в OutputStream, заданное proc.getOutputStream().Чтобы получать выходные данные из подпроцесса, у меня в основном есть цикл while, который читает из подпроцесса stdout:

while ((b = br.read()) != -1) {
    System.out.println("Read "+b);
    bb[0] = (byte) b;
    // call an event listener with the read byte
    listener.dataReceived(bb);
}

Это работает, только , если я немедленно сбрасываю вывод на оба заканчиваются.То есть я должен очищать каждый пользовательский ввод, а подпроцесс должен сбрасывать свой собственный stdout, чтобы что-то произошло.В противном случае read() блокирует ожидающие данные, которые фактически никогда не отправляются (стандартный вывод подпроцесса просто сохраняет буферизацию).Как запустить I / O?

Пример подпроцесса терминала:

#include <stdio.h>

int main() {
    char c;
    while((c = getchar()) != -1) {
        printf("Got: %d\n", c);
        // doesn't work in my Java program if the next line isn't present
        fflush(stdout);
    }
    return 0;
}

Я работаю на Ubuntu 10.10 с Sun Java 6.

Ответы [ 3 ]

4 голосов
/ 20 ноября 2010

Вы не можете прочитать данные из файла, пока данные не будут записаны на диск.Вы не можете читать данные из сокета или канала, пока данные не будут помещены в буфер канала / сокета.

Ваша Java-программа не имеет никакого контроля (*), когда внешний процесс сбрасывает свой вывод и записывает данные вдисковый / конвейерный буфер / сокетный буфер.Вы полностью зависите от поведения буфера внешней программы.Это верно для каждой операционной системы и для каждого языка программирования.

Каждый сетевой программист должен иметь дело с этим, так что просто справьтесь с этим.

(*) - Иногда некоторые программы (например, cat для одного) имеют опции (-u) для указания программе использовать небуферизованный вывод.В противном случае вы милосердие

1 голос
/ 19 марта 2011

Многие многие библиотеки времени выполнения (например, я знаю, что libc делает это, и не удивлюсь, если другие тоже сделают это) будут буферизовать свои выходные данные по умолчанию за исключением , когда выходные данные поступают на терминал , Это чрезвычайно увеличивает эффективность обработки данных при работе со многими линиями (например, в обычном конвейере), но когда имеется только небольшое количество информации, это очень больно. Если у вас есть доступ к источнику подпроцесса, определенно лучше обновить код, отключив буферизацию или добавив сбросы.

Но это не всегда возможно, особенно когда речь идет о стороннем коде. Лучшее прочее исправление, которое я знаю в этом случае, заключается в использовании инструмента типа Expect для обмана подпроцесса. Внутренне, Expect знает, как притвориться терминалом (используя ptys в Unix и безбожные хаки в Windows), таким образом обманывая другие программы, отключая (или, по крайней мере, уменьшая) их буферизацию. Существует сценарий - unbuffer - для Expect, который фокусирует его именно на такого рода использовании. (В целом, это может сделать гораздо больше, чем просто справиться с непослушной буферизацией, но в любом случае это лучшее исправление.)

0 голосов
/ 21 ноября 2010

Вы не запускаете свой цикл чтения ввода-вывода из потока диспетчеризации событий?уже занимаюсь этим).Скорее всего, лучше всего сразу переключаться на подпроцесс при каждом нажатии клавиши GUI;если вы не хотите поддерживать что-то вроде «читать всю строку за раз».

...