Напівсинхронна труба


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?

EDIT

Можливо, цього було б достатньо, якби я міг надійно дізнатись ідентифікатор процесу c(або b) процесу. Тоді всю трубу можна було запустити асинхронно, і я міг зачекати ідентифікатора процесу. Щось по лінії

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

РЕДАКЦІЯ ^ 2

Я знайшов рішення, коментарі (або ще кращі рішення) вітаються.


Ви маєте на увазі, що хочете dпочати обробку cрезультатів лише після cзавершення? Ви не хочете dпочинати обробляти кожен вихідний рядок, як він є?
terdon

@terdon: Ні, dможна запускати коли завгодно, але cпотрібно закінчити, перш ніж я можу рухатись далі.
krlmlr

Це здається суперечливим, якщо dможна починати, коли це подобається, що саме ти чекаєш?
тердон

@terdon: розгорнуто для показу випадків використання.
krlmlr

Якщо dне використовується вихід, cто, здається, немає сенсу робити dчастину трубопроводу. Але якщо ви dвикористовуєте вхід, тоді вам dслід попрацювати деякий час над його входом, прочитавши все це, щоб ваш підхід змінився.
Hauke ​​Laging

Відповіді:


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 Який стан перегонів?
Hauke ​​Laging

notifyмогло бути виконано до того, як я встановив сигнальну пастку. Як я можу зробити так, щоб не пропустити цей сигнал?
krlmlr

@krlmlr Ви зупиняєте скрипт, який викликає конвеєр, поки не отримає сам сигнал, який надсилається після того, trapяк було визначено.
Hauke ​​Laging

Ваша редакція: Труба викликається в циклі, над яким я не маю управління. На жаль, { 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.


0

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

a | b | c | sponge | d

Губка буде до кінця cвиходу, перш ніж прокладати його d. Сподіваюся, що ви цього хотіли.


0

Ви можете просто зробити:

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

Отже, коли dзакінчується, catволя буде поглинати решту cрезультатів, поки вона не закінчиться.

В порядку. Після коментарів, я думаю, що відповідь полягає у тому, щоб безпосередньо почати dна задньому плані.

Зробіть:

a | b | c | (d &)

або скористайтеся рішенням Стефана Шазеласа , якщо є проблеми з dчитанням з stdin .


Я боюсь, що мій випадок використання - це навпаки: cзакінчується раніше d, і мені доводиться чекати, поки cзакінчується, але мені все одно d.
krlmlr

Вибачте, я не розумію. Що ви хочете робити, коли cзакінчується і dвсе ще працює? Вбити d?
angus

dмає графічний інтерфейс і може працювати, поки користувач не закриє його.
krlmlr

Гаразд ... але це вже відбувається. Коли a, bі cзакінчують свою роботу, вони закривають stdout і виходять; і dзалишається лише запущеним. Ви хочете відправити dна задній план після cзавершення? Є те, що його?
angus

Так, dслід надсилати на задній план після cзакінчення.
krlmlr
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.