Завершение процесса при завершении другого процесса

Вопрос или проблема

Учитывая 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

Объяснение работы данного подхода:

  1. Создание пайпа: Использование конвейера cmdA | позволяет передать вывод команды cmdA непосредственно в программу, которая запускается на следующем этапе (в вашем случае, это fzf).

  2. Отслеживание процессов: Использование библиотеки IO::Poll в Perl обеспечивает контроль за состоянием потоков. В частности, она позволяет ожидать, пока данные не будут полностью переданы через пайп.

  3. Управление сигналами: Установка обработчика сигнала для обработки выхода дочернего процесса через $SIG{CHLD} позволяет правильным образом завершать родительский процесс.

  4. Долгий вывод: После завершения cmdA, скрипт отправит сигнал TERM на процесс b. Это остановит его, но если b не завершится, через 1 секунду будет отправлен принудительный сигнал KILL.

Важные примечания:

  • Закрытие пайпа: Убедитесь, что команда cmdA завершает вывод и закрывает пайп, прежде чем сигнализировать процессу b. Это гарантирует, что все данные будут обработаны.

  • Синхронизация: Если вы заметите, что сигнал на завершение иногда приходит слишком рано (например, если b не успевает обработать данные), рассмотрите возможность увеличения времени ожидания между TERM и KILL, или реализуйте цикл ожидания, проверяющий, смог ли b обработать данные.

  • Тестирование: Всегда проверяйте свой сценарий с разными параметрами и данными, чтобы убедиться, что процесс b завершает свою работу корректно и не теряет данные в процессе исполнения.

Следуя этому примеру, вы сможете успешно управлять завершением интерактивного процесса при завершении другого процесса в UNIX-подобных операционных системах.

Оцените материал
Добавить комментарий

Капча загружается...