Git вывод stderr не может
Я пишу графический обработчик URI для git://связей с bash и zenity, и я использую диалог zenity 'text-info' для отображения вывода clone git во время его работы, используя трубопроводы FIFO. script длиной около 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-тексте показывают все выходные данные постепенно.
git
выводит сообщения о выполнении (ничего, кроме сообщения "Инициализировано" ) на 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, но пока еще не решили проблему. На данный момент, используя один, другой или оба, работает теоретически, но на практике зенит вообще ничего не показывает.
Исправить попытку 2
@mvds предположил, что использование обычного файла в комбинации с tail -f
, а не cat
может сделать это. Удивлен в таком простом решении (спасибо!) Я попробовал это, но, к сожалению, только первая строка появилась в зените и ничего больше.
Исправить попытку 3
После выполнения какого-либо анализа и проверки исходного кода git, я понимаю, что git выводит всю информацию о его ходе (что-либо за сообщением "Инициализировано" ) на stderr и тот факт, что это первая строка и мое предположение о том, что это из-за ухода кошки на раннем этапе EOF было совпадением/ошибочным предположением (git не EOF, пока программа не закончится).
Ситуация, казалось, стала намного проще, поскольку мне не нужно было ничего менять с исходного кода (в начале вопроса), и он должен работать. Однако загадочно, что вывод stderr "исчезает" при перенаправлении - и это только то, что происходит в git.
Тестовая версия? Попробуйте это и посмотрите, видите ли что-нибудь в файле (вы не будете):
git clone git://anongit.freedesktop.org/xorg/proto/dri2proto 2> hurr
Это противоречит всему, что я знаю о stderr и перенаправлении; Я даже написал небольшую программу на C, которая выводит на stderr и stdout, чтобы доказать себе, что перенаправление просто не работает для git.
Исправить попытку 4
В ответ на ответ Jakub Narębski, а также ответы на письма, отправленные в список рассылки git, --progress
- это нужная мне опция. Обратите внимание, что этот параметр работает только после команды, а не до clone
.
Успех!
Большое спасибо за вашу помощь. Это фиксированная линия:
git clone "$1" "$target" --progress > /tmp/githandler-fifo 2>&1 &
![u1AW3.png]()
Ответы
Ответ 1
Я думаю, что по крайней мере некоторые отчеты о проделанной работе заставляют замолчать, когда вывод не является терминалом (tty). Я не уверен, относится ли это к вашему делу, но попробуйте передать --progress
вариант git clone '(т.е. Использовать git clone --progress <repository>
).
Хотя я не знаю, было ли это то, что вы хотели иметь.
Ответ 2
С одной стороны, перенаправление вывода анализируется справа налево, поэтому
git clone "$1" "$target" 2>&1 > /tmp/githandler-fifo &
не равно
git clone "$1" "$target" > /tmp/githandler-fifo 2>&1 &
Последний перенаправляет stderr на stdout, а затем stdout (включая stderr) в файл. Первый будет перенаправлять stdout в файл, а затем show stderr на stdout.
Что касается трубопровода на zenity
(чего я не знаю), я думаю, что вы, возможно, слишком сильно усложняете именованный канал. Использование strace
может пролить свет на внутреннюю работу процессов, которые вы запускаете. Для неопытных, названных каналов ухудшаются по сравнению с обычными трубами.
Ответ 3
Учитывая эксперимент с FIFO под названием 'a', я думаю, что проблема заключается в том, как zenity обрабатывает свой вклад. Что произойдет, если вы начнете вводить слово с клавиатуры? (Подозрительный: он ведет себя так, как вам хотелось бы, читая EOF.) Однако может случиться, что zenity обрабатывает вход терминала (вход tty) с использованием обычного блокирующего ввода-вывода, но использует неблокирующий ввод-вывод для всех других типов устройств. Неблокирующий ввод-вывод подходит для ввода из файлов; он менее желателен для ввода из труб или FIFO и т.д. Если бы он использовал неблокирующий ввод-вывод, zenity получал первую строку вывода, а затем выходил из цикла, думая, что это было сделано, потому что его вторая попытка чтения указывала бы, что больше ничего не было доступно.
Демонстрация того, что это то, что происходит (или нет), будет сложной задачей. Я бы посмотрел на "truss" или "strace" или другой монитор системных вызовов, чтобы узнать, что делает zenity.
Что касается обходных путей... если гипотеза правильная, тогда вам нужно будет убедить зенит, что она читает с терминала, а не с FIFO, поэтому вам, вероятно, придется подстроить псевдо-tty (или псевдотерминал); первый процесс будет записываться в мастер-конец pty, и вы сможете настроить zenity для чтения с ведомого конца pty. Вы все равно можете использовать FIFO - хотя он делает длинную цепочку команд.