Надежный способ получить PID конвейерного фонового процесса

В то время как большинство оболочек поддерживают синхронизацию внешних и встроенных команд, zsh не поддерживает синхронизацию для встроенных команд.

Если вам нужно получить результат времени, вы можете позвонить

time /bin/true

или отправьте отчет об ошибке людям из zsh.

С новой информацией все выглядит иначе.

Сложные команды синхронизации работают только в том случае, если timeявляется зарезервированным словом в оболочке.

Таким образом, у вас есть два ограничивающих фактора

  • оболочка должна иметь возможность синхронизировать встроенные команды

  • оболочка должна реализовать timeкак зарезервированное слово, улучшающее синтаксис оболочки

Это сокращает список потенциальных оболочек до:

bosh bash mksh ksh

Для встроенных команд оболочка должна знать, как измерять собственное время.

Для сложных команд оболочка должна реализовать timeвнутри интерпретатора.

1
01.12.2021, 15:28
1 ответ

Когда фоновое задание представляет собой конвейер в форме cmd1 | cmd2, оно по-прежнему остается одним фоновым заданием. Невозможно узнать, когда начнется cmd1.

Каждый &создает одно фоновое задание. Как только cmd &возвращается, оболочка знает о том, что фоновое задание:cmd & jobsперечисляет cmd. cmd & pid=$!задает pidидентификатор процесса, который выполняет cmd.

Конвейер cmd1 | cmd2создает еще два подпроцесса :один для запуска cmd1и один для запуска cmd2. Оба процесса являются потомками подпроцесса, выполняющего фоновое задание. Вот как выглядит дерево процессов дляbash -c '{ sleep 123 | sleep 456; } & jobs -p; sleep 789':

 PID PPID CMD
 268  265  |   \_ bash -c { sleep 123 | sleep 456; } & sleep 789
 269  268  |       \_ bash -c { sleep 123 | sleep 456; } & sleep 789
 270  269  |       |   \_ sleep 123
 271  269  |       |   \_ sleep 456
 272  268  |       \_ sleep 789

268 — это исходный процесс bash. 269 ​​— это фоновое задание, которое jobs -pпечатает. 270 и 271 — это левая -и правая -стороны канала, оба дочерние по отношению к основному процессу фонового задания (269 ).

Версия bash, которую я тестировал с (5.0.17 в Linux ), оптимизирует cmd1 | cmd2 &без фигурных скобок. В этом случае левая -сторона конвейера выполняется в том же процессе, что и фоновое задание :

.
 PID PPID CMD
 392  389  |   \_ bash -c sleep 123 | sleep 456 & jobs -p; sleep 789
 393  392  |       \_ sleep 123
 394  392  |       \_ sleep 456
 395  392  |       \_ sleep 789

Нельзя полагаться на то, что это поведение будет стабильным в разных версиях bash или, возможно, даже в разных платформах, дистрибутивах, версиях libc и т. д.

jobs -p %cmd1ищет задание, код которого начинается с cmd1. Он находит cmd1 | cmd2. jobs -p %?cmd2находит ту же работу¹. Невозможно получить доступ к идентификаторам процессов, запущенных cmd1и cmd2, через встроенные -функции bash.

Если вам нужно точно знать, что cmd1запущен, используйте подстановку процесса.

cmd1 >(cmd2)

Вы не узнаете, когда cmd2начинается и заканчивается.

Если вам нужно знать, когда cmd1и cmd2начинаются и заканчиваются, вам нужно сделать их обе заданиями и заставить их обмениваться данными через именованный канал.

tmp=$(mktemp -d) # Remove this in cleanup code
mkfifo "$tmp/pipe"
cmd1 >"$tmp/pipe" & pid1=$!
cmd2 <"$tmp/pipe" & pid2=$!
…

Команда jobsне очень полезна в сценариях. Используйте $!, чтобы запомнить PID фоновых заданий.

¹ Или, по крайней мере, должен. Моя версия жалуется на неоднозначную спецификацию работы,что должно быть ошибкой, поскольку он говорит об этом, несмотря на то, что существует только одна работа.

2
01.12.2021, 17:07

Теги

Похожие вопросы