Полусинхронная труба

11

Предположим, у меня есть следующая труба:

a | b | c | d

Как я могу ждать завершения c(или b) в shили bash? Это означает, что скрипт dможет запускаться в любое время (и его не нужно ждать), но cдля корректной работы требуется полный вывод из .

Вариант использования предназначен difftoolдля gitсравнения изображений. Он вызывается gitи должен обработать свой ввод ( a | b | cчасть) и отобразить результаты сравнения ( dчасть). Вызывающая сторона удалит ввод, который требуется для aи b. Это означает, что перед возвратом из сценария процесс c(или b) должен завершиться. С другой стороны, я не могу ждать, dпотому что это означает, что я жду ввода пользователя.

Я знаю, что могу записать результаты cво временный файл или, возможно, использовать FIFO в bash. (Однако не уверен, что FIFO поможет.) Можно ли добиться этого без временных файлов sh?

РЕДАКТИРОВАТЬ

Возможно, было бы достаточно, если бы я мог достоверно узнать идентификатор процесса c(или b) процесса. Тогда весь канал мог бы быть запущен асинхронно, и я мог ждать идентификатор процесса. Нечто подобное

wait $(a | b | { c & [print PID of c] ; } | d)

EDIT ^ 2

Я нашел решение, комментарии (или еще лучшие решения) приветствуются.

krlmlr
источник
Вы имеете в виду, что хотите dначать обработку cвывода только после cзавершения? Вы не хотите dначинать обрабатывать каждую строку вывода, как она есть?
Terdon
@terdon: Нет, dможно начинать, когда захочется , но cнужно закончить, прежде чем я смогу двигаться дальше.
krlmlr
Это кажется противоречивым, если вы dможете начать, когда захотите, чего именно вы ждете?
Terdon
@terdon: расширен, чтобы показать вариант использования.
krlmlr
Если dне использовать выходные данные, cто, кажется, не имеет никакого смысла делать dчасть конвейера. Но если dон использует входные данные, то dдолжен некоторое время поработать над их вводом после прочтения всего этого, чтобы ваш подход имел какое-либо значение.
Хауке Лагинг

Ответы:

6
a | b | { c; [notify];} | d

Уведомление может быть сделано, например, с помощью сигнала PID, который был передан в переменной среды ( kill -USR1 $EXTPID), или путем создания файла ( touch /path/to/file).

Еще одна идея:

Вы выполняете следующий процесс (тот, который можно запустить, которого вы ожидаете) из конвейера:

a | b | { c; exec >&-; nextprocess;} | d

или

a | b | { c; exec >&-; nextprocess &} | d
Хауке Лагинг
источник
Благодарю. Но затем создание временного файла для промежуточного вывода более многословно и проще для анализа (для человека). Кроме того, как я могу избежать состояния гонки с сигналом? ... Я надеялся на более чистое решение, но если это путь, то пусть будет так.
krlmlr
@krlmlr Какое состояние гонки?
Хауке Лагинг
notifyможет быть выполнено до того, как я настрою ловушку сигнала. Как я могу убедиться, что не пропустил этот сигнал?
krlmlr
@krlmlr Вы останавливаете скрипт, который вызывает конвейер, пока он не получит сам сигнал, который отправляется после того, trapкак был определен.
Хауке Лагинг
Ваше редактирование: канал вызывается в цикле, который я не могу контролировать. К сожалению, { c; bg; }или { c; exit 0; }, кажется, не работает.
krlmlr
5

Если я правильно понимаю ваш вопрос, это должно сработать:

a | b | c | { (exec <&3 3<&-; d) &} 3<&0

(хитрость fd 3 в том, что некоторые (большинство) оболочек перенаправляют stdin в / dev / null с помощью &).

Стефан Шазелас
источник
4

В bash вы можете использовать процесс подстановки . Команда в процессе подстановки выполняется асинхронно, ее не ждут.

a | b | c > >(d)
Жиль "ТАК - перестань быть злым"
источник
Благодарю. Это bashтолько верно - без shподдержки?
krlmlr
@krlmlr только Bash / zsh (и ksh93 с некоторыми изменениями).
Жиль "ТАК - перестань быть злым"
3

Это то, что я нашел методом проб и ошибок с помощью ввода Хауке:

a | b | { c; kill -PIPE $$; } | d

Эквивалентное:

a | b | ( c; kill -PIPE $$; ) | d

(Последнее является более явным, потому что {}все равно будет работать в подоболочке, если внутри трубы.)

Некоторые другие сигналы (в том числе QUIT, TERMи USR1) тоже работают, однако в этом случае описание сигнала отображается на терминале.

Интересно, является ли это изначальным намерением PIPEсигнала. Согласно инструкции :

SIGPIPE: PIPEСигнал отправляется процессу, когда он пытается записать в канал без процесса, подключенного к другому концу.

Это означает, что когда я искусственно посылаю сигнал канала в подоболочку, он молча завершается, оставляя конечного потребителя в dпокое ( ).

Это работает в обоих shи bash.

krlmlr
источник
0

Вы можете использовать spongeпрограмму из пакета "moreutils":

a | b | c | sponge | d

Губка будет в конце cвыходного, прежде чем обвязать его d. Надеюсь, это то, что вы хотели.

Minijackson
источник
0

Вы можете просто сделать:

a | b | c | (d ; cat > /dev/null)

Таким образом, когда он завершится d, он catбудет поглощать остальную часть cвыходных данных до тех пор, пока не завершится.

ОК. После комментариев, я думаю, что ответ должен начать прямо dв фоновом режиме.

Делать:

a | b | c | (d &)

или используйте решение Stephane Chazelas , если есть проблемы с dчтением из stdin .

ангус
источник
Я боюсь, что мой вариант использования скорее наоборот: cзаканчивается раньше, чем dнужно, и я должен ждать до cконца, но мне все равно d.
krlmlr
Извини, я не понимаю. Что вы хотите сделать, когда cзаканчивается и dвсе еще работает? Убить d?
ангус
dимеет графический интерфейс и может работать, пока пользователь не закроет его.
krlmlr
ОК ... но это уже происходит. Если a, bи cзакончить свою работу, они близко стандартный вывод и выход; и только dработает. Вы хотите отправить dв фон после cокончания? Это оно?
ангус
Да, dдолжны быть отправлены в фоновом режиме после cзавершения.
krlmlr