Вопрос или проблема
Учитывая a | b
, я хотел бы завершить b
после завершения a
. b
является интерактивным процессом, который не заканчивается, когда a
завершено (fzf
в моем случае), и вся конструкция a | b
выполняется в подшелле $()
.
Пока что я пришел к следующему решению:
echo $({ sleep 5 & a=$!; { wait $a; kill $b; } } | { fzf & b=$!; })
sleep
представляет a
, а fzf
представляет b
, результат в примере используется функцией echo
, но в моем случае это будет аргументом для ssh
. Кажется, что $b
не является PID fzf
, он пуст. Насколько я понимаю, это не должно быть так, поскольку я использовал {}
, а не ()
, так что это не выполняется в подшелле.
Вы можете сделать так:
cmdA |
perl -MIO::Poll -e '
$SIG{CHLD} = sub{wait; exit($? & 127 ? 128|($?&127) : $?>>8)};
$p = IO::Poll->new;
$p->mask(STDIN, POLLHUP);
if ($pid = fork) {
$p->poll;
kill "TERM", $pid;
sleep 1;
kill "KILL", $pid;
pause;
} else {
exec @ARGV;
}' -- cmdB
Тем не менее, если cmdA
выводит что-то непосредственно перед завершением, и в тот момент cmdB
не считывает данные из канала, существует вероятность, что cmdB
будет завершен, прежде чем у него появится возможность это прочитать, не говоря уже о том, чтобы это обработать, поэтому вам может понадобиться немного подождать после закрытия канала и перед его завершением, чтобы дать cmdB
время на чтение и обработку всего вывода cmdA
.
Вы также можете решить не завершать его, пока канал не будет опустошен, что вы можете проверять в цикле с помощью FIONREAD
ioctl()
(а затем, возможно, все же дать ему дополнительное время для обработки).
Ответ или решение
Для решения задачи, когда требуется завершить процесс b
, после того как процесс a
завершится, можно использовать различные подходы, включая синхронизацию процессов и управление сигналами. В вашем случае, процесс a
является командой, которая выдает вывод в конвейер, а процесс b
является интерактивной программой (например, fzf
), которая не завершает свою работу автоматически.
Вот один из примеров реализации данного сценария на основе кода на Perl, который позволяет наглядно понять, как это можно сделать:
cmdA |
perl -MIO::Poll -e '
$SIG{CHLD} = sub { wait; exit($? & 127 ? 128 | ($? & 127) : $? >> 8) };
$p = IO::Poll->new;
$p->mask(STDIN, POLLHUP);
if ($pid = fork) {
$p->poll; # Ожидаем, пока процесс A завершится
kill "TERM", $pid; # Присылаем сигнал завершения процессу B
sleep 1; # Даем время процессу B обработать данные
kill "KILL", $pid; # В случае, если процесс не завершился, отправляем принудительное завершение
pause; # Ожидаем завершения процесса
} else {
exec @ARGV; # Запуск процесса B
}' -- cmdB
Объяснение работы данного подхода:
-
Создание пайпа: Использование конвейера
cmdA |
позволяет передать вывод командыcmdA
непосредственно в программу, которая запускается на следующем этапе (в вашем случае, этоfzf
). -
Отслеживание процессов: Использование библиотеки
IO::Poll
в Perl обеспечивает контроль за состоянием потоков. В частности, она позволяет ожидать, пока данные не будут полностью переданы через пайп. -
Управление сигналами: Установка обработчика сигнала для обработки выхода дочернего процесса через
$SIG{CHLD}
позволяет правильным образом завершать родительский процесс. -
Долгий вывод: После завершения
cmdA
, скрипт отправит сигналTERM
на процессb
. Это остановит его, но еслиb
не завершится, через 1 секунду будет отправлен принудительный сигналKILL
.
Важные примечания:
-
Закрытие пайпа: Убедитесь, что команда
cmdA
завершает вывод и закрывает пайп, прежде чем сигнализировать процессуb
. Это гарантирует, что все данные будут обработаны. -
Синхронизация: Если вы заметите, что сигнал на завершение иногда приходит слишком рано (например, если
b
не успевает обработать данные), рассмотрите возможность увеличения времени ожидания междуTERM
иKILL
, или реализуйте цикл ожидания, проверяющий, смог лиb
обработать данные. -
Тестирование: Всегда проверяйте свой сценарий с разными параметрами и данными, чтобы убедиться, что процесс
b
завершает свою работу корректно и не теряет данные в процессе исполнения.
Следуя этому примеру, вы сможете успешно управлять завершением интерактивного процесса при завершении другого процесса в UNIX-подобных операционных системах.