Вопрос или проблема
Я пытаюсь использовать косопроцессы, чтобы передать вывод асинхронных процессов в другие команды оболочки, а затем проверить код завершения асинхронного процесса:
coproc LS { ls dir; }
while IFS= read i; do echo "$i"; done <&"$LS" # Делать что-то с выводом.
wait "$LS_PID"; echo $? # Проверить код завершения.
Проблема в том, что приведенный выше код не работает, по крайней мере не с Bash 4.3.46(1)-release
. После того, как оператор while
выполнен, Bash сбросит $LS_PID
(возможно, потому что замечает, что процесс завершился). Вызов wait
для процессов все еще работает, например:
coproc LS { ls dir; }
P=$LS_PID
while IFS= read i; do echo "$i"; done <&"$LS" # Делать что-то с выводом.
wait "$P"; echo $? # Проверить код завершения.
Это выведет 0
или 2
в зависимости от того, существует ли каталог. Это всего лишь неудобство, что приходится сохранять PID в другой переменной. Но мне интересно, почему это происходит и могу ли я это как-то изменить.
Почему $LS_PID
сбрасывается до того, как я вызову wait
на процессе? Задокументировано ли это поведение где-то? Существует ли простой обходной путь, потому что, по моему мнению, это распространенный случай использования: и использовать вывод команды, и проверять ее код завершения.
Это запланированное поведение переменной COPROC_PID
, хотя оно не задокументировано в руководстве Bash.
Правильное решение — это делать именно то, что вы сделали: сохранять значение $COPROC_PID
в переменной, которая не будет сброшена, когда косопроцесс завершится.
Обратите внимание, что это решение не сработает, если косопроцесс завершится до того, как вы успеете получить его PID, поэтому может быть разумно удостовериться, что ваша переменная имеет значение перед использованием.
Ссылка: https://lists.gnu.org/archive/html/bug-bash/2012-12/msg00037.html
Ответ или решение
Теория
Когда вы работаете с сопроцессами в Bash, используются специальные переменные, такие как COPROC_PID
, которые хранят идентификатор процесса (PID) созданного сопроцесса. Эти переменные позволяют отслеживать статус выполнения процесса и осуществлять синхронизацию при помощи команды wait
. Однако в Bash существует поведение, где переменная COPROC_PID
, которая хранит PID процесса, обнуляется или исчезает после завершения выполнения соответствующего сопроцесса. Это связано с тем, что оболочка Bash отслеживает завершение процесса и автоматически удаляет переменную, как только процесс завершается.
Пример
Рассмотрим ваш код:
coproc LS { ls dir; }
while IFS= read i; do echo "$i"; done <& "$LS" # Обработка выхода процесса
wait "$LS_PID"; echo $? # Проверка кода возврата
Ваш код имеет структуру, в которой сопроцесс LS создается и его вывод обрабатывается в цикле while
. После обработки ожидается завершение процесса и проверяется его код возврата. Однако LS_PID
будет удален из-за описанного поведения Bash, поскольку Bash обнаруживает, что процесс завершился, и очищает связанные с ним переменные, включая COPROC_PID
.
Применение
Причины
Причина подобного поведения связана с внутренней логикой работы Bash. Когда процесс завершается, оболочка считает, что в дальнейшем работа с ним и его PID не будет необходима, и автоматически освобождает связанные ресурсы. Документация Bash не всегда подробно описывает данные аспекты, что может привести к путанице среди пользователей.
Решения
Наиболее прямолинейным и эффективным решением является сохранение значения COPROC_PID
в другой переменной до того, как процесс завершится. Пример правильного подхода, который вы предложили:
coproc LS { ls dir; }
P=$LS_PID # сохраняем значение в другой переменной
while IFS= read i; do echo "$i"; done <& "$LS"
wait "$P"; echo $? # теперь работаем с переменной P
Такой подход гарантирует, что идентификатор процесса будет сохранен и доступен для дальнейшей обработки, даже если Bash удалит COPROC_PID
.
Рекомендации
- Всегда сохраняйте PID сопроцесса в отдельную переменную сразу после его создания, чтобы избежать потери информации.
- Убедитесь, что переменная, в которую вы сохраняете PID, имеет значение до выполнения команды
wait
, это позволит избежать ошибок в неудачных запуске команды. - Регулярно обновляйте знания о версии Bash и изменениях в работе командной оболочки, так как поведение может меняться между обновлениями.
Заключение
Работа с сопроцессами в Bash предоставляет значительные возможности, однако также выдвигает определенные требования к организацию процесса. Понимание внутреннего поведения Bash и правильное сохранение необходимых данных позволят избежать распространенных проблем и обеспечат успешное управление асинхронными задачами. Ваша стратегия сохранения PID процесса в отдельную переменную является правильной и рекомендуется для использования в профессиональной практике.