Выход git stderr не может передать

Я пишу графический обработчик URI для ссылок git:// с bash и zenity, и я использую диалоговое окно zenity 'text-info', чтобы показать вывод клонов git во время его работы, используя FIFO-трубопровод. Сценарий длиной около 90 строк, поэтому я не буду публиковать его здесь, но вот самые важные строки:

git clone "$1" "$target" 2>&1 | cat >> /tmp/githandler-fifo &
cat /tmp/githandler-fifo | zenity --text-info --text='Cloning git repository' &

Я использую FIFO вместо прямой трубы, чтобы позволить им работать асинхронно и убивать git, если окно zenity закрыто.

Проблема в том, что единственная строка, которая появляется из вывода git, является первой:

Initialized empty Git repository in /home/delan/a/.git/

Другие строки с подсчетом объектов и т. Д. Не отображаются или отображаются на терминале.

Текущая причина

Нынешний консенсус относительно того, почему это не работает, кажется, что cat является неблокирующим и выходит после первой строки, передавая его только zenity, а не остальным. Моя цель состоит в том, чтобы принудительно блокировать чтение, и чтобы диалоговое окно с текстовой информацией zenity показывало все результаты постепенно.

git выводит сообщения о ходе выполнения (что угодно, кроме сообщения "Initialized") на stderr, но в тот момент, когда я пытаюсь передать stderr в файл или объединить с stdout, сообщения исчезают.

Исправить попытку 1

Я попытался написать две блокирующие версии функций cat на C, bread и bwrite, например:

#include <stdio.h>
main(int argc, char **argv) {
    int c;
    for (;;) {
        freopen(argv[1], "r", stdin);
        while ((c = getchar()) != EOF)
            putchar(c);
    }
}

#include <stdio.h>
main(int argc, char **argv) {
    int c;
    for (;;) {
        freopen(argv[1], "w", stdout);
        while ((c = getchar()) != EOF)
            putchar(c), fputs("writing", stderr);
    }
}

Они хорошо работают, потому что блокируют и не выходят из EOF, но это еще не решило проблему. В настоящее время использование одного, другого или обоих работает в теории, но на практике zenity сейчас вообще ничего не показывает.

Исправить попытку 2

@mvds предложил использовать обычный файл в сочетании с tail -f скорее, чем catможет сделать это. Удивленный таким простым решением (спасибо!), Я попробовал его, но, к сожалению, только первая строка показалась в зените и больше ничего.

Исправить попытку 3

После некоторого изучения и проверки исходного кода git, я понимаю, что git выводит всю свою информацию о прогрессе (что-либо после сообщения "Initialized") на stderr, а также тот факт, что это первая строка и мое предположение, что это из-за cat преждевременное завершение EOF было совпадением / ошибочным предположением (git не EOF, пока программа не завершится).

Ситуация, казалось, стала намного проще, поскольку мне не нужно было ничего менять по сравнению с исходным кодом (в начале вопроса), и это должно работать. Однако при загадочных обстоятельствах вывод stderr "исчезает" при перенаправлении - и это только то, что происходит в git.

Прецедент? Попробуйте это, и посмотрите, видите ли вы что-нибудь в файле (вы не увидите):

git clone git://anongit.freedesktop.org/xorg/proto/dri2proto 2> hurr

Это противоречит всему, что я знаю о stderr и перенаправлении; Я даже написал небольшую C-программу, которая выводит на stderr и stdout, чтобы доказать себе, что перенаправление просто не работает для git.

Исправить попытку 4

В соответствии с ответом Якуба Наренбского, а также ответами на электронные письма, которые я отправил в список рассылки git, --progress это вариант, который мне нужен. Обратите внимание, что эта опция работает только после команды, а не до clone,

Успех!

Большое спасибо за вашу помощь. Это фиксированная линия:

git clone "$1" "$target" --progress > /tmp/githandler-fifo 2>&1 &

3 ответа

Решение

Я думаю, что, по крайней мере, некоторые из отчетов о ходе работы отключаются, когда вывод не является терминалом (tty). Я не уверен, относится ли это к вашему делу, но попробуйте пройти --progress возможность "git clone" (т.е. использовать git clone --progress <repository>).

Хотя я не знаю, хотел ли ты этого.

С одной стороны, перенаправление вывода анализируется справа налево, поэтому

git clone "$1" "$target" 2>&1 > /tmp/githandler-fifo &

не равно

git clone "$1" "$target" > /tmp/githandler-fifo 2>&1 &

Последний перенаправит stderr в stdout, а затем stdout (включая stderr) в файл. Первый перенаправит стандартный вывод в файл, а затем покажет стандартный вывод на стандартный вывод.

Что касается трубопроводов zenity (что я не знаю), я думаю, вы можете сделать вещи слишком сложными с именованным каналом. С помощью strace может пролить свет на внутреннюю работу процессов, которые вы запускаете. Для неопытных именованные каналы ухудшают ситуацию по сравнению с обычными.

Учитывая эксперимент с FIFO под названием "a", я думаю, что проблема заключается в том, как zenity обрабатывает свой вклад. Что произойдет, если вы введете в zenity с клавиатуры? (Подозрение: при чтении в EOF он ведет себя так, как вы хотите.) Однако, возможно, что zenity обрабатывает ввод с терминала (tty-ввод), используя обычный блокирующий ввод / вывод, но использует неблокирующий ввод / вывод для всех других типов устройств. Неблокирующий ввод / вывод хорош для ввода из файлов; это менее желательно для ввода из каналов или FIFO и т. д. Если бы он использовал неблокирующий ввод / вывод, zenity получил бы первую строку вывода, а затем вышел из цикла, думая, что это было сделано, потому что его вторая попытка чтения показала бы, что больше ничего не было доступно сразу.

Демонстрировать, что это происходит (или нет), будет сложно. Я бы искал 'truss' или 'strace' или другой монитор системных вызовов, чтобы отследить, что делает zenity.

Что касается обходных путей... если гипотеза верна, то вам нужно будет убедить zenity в том, что он читает с терминала, а не с FIFO, так что вам, вероятно, нужно будет настроить псевдотетти (или pty); первый процесс записывает в главный конец pty, и вы организуете чтение для zenity из подчиненного конца pty. Вы все еще можете использовать FIFO - хотя он делает длинную цепочку команд.

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