Да, в том же bash
духе ksh
(откуда берется эта особенность) процессы внутри подстановки процессов не ожидаются (перед выполнением следующей команды в сценарии).
для <(...)
одного, это обычно хорошо, как в:
cmd1 <(cmd2)
оболочка будет ожидать cmd1
и cmd1
будет обычно ожидать, поскольку cmd2
она читает до конца файла в канале, который заменяется, и этот конец файла обычно происходит, когда cmd2
умирает. Это та же причина , несколько снарядов (не bash
) не беспокоить ждут cmd2
в cmd2 | cmd1
.
Для cmd1 >(cmd2)
, однако, что это вообще не так, как это более , cmd2
что , как правило , ждет cmd1
там так будет вообще выход после.
Это исправлено в zsh
том, что ждет cmd2
там (но не, если вы пишете это как cmd1 > >(cmd2)
и cmd1
не встроено, используйте{cmd1} > >(cmd2)
вместо этого как документировано ).
ksh
не ждет по умолчанию, но позволяет ждать его с помощью wait
встроенного (это также делает pid доступным в$!
, хотя это не поможет, если вы это сделаете cmd1 >(cmd2) >(cmd3)
)
rc
(с cmd1 >{cmd2}
синтаксисом), так же, какksh
вы можете получить pids всех фоновых процессов с $apids
.
es
(также с cmd1 >{cmd2}
) ждет, cmd2
как в zsh
, а также ждетcmd2
<{cmd2}
перенаправления в процессе.
bash
делает pid cmd2
(или, точнее, подоболочки, когда она запускаетсяcmd2
в дочернем процессе этого subshell, даже если это последняя команда) $!
, но не позволяет вам ждать его.
Если вам нужно использовать bash
, вы можете обойти проблему, используя команду, которая будет ожидать обеих команд с:
{ { cmd1 >(cmd2); } 3>&1 >&4 4>&- | cat; } 4>&1
Это делает оба, cmd1
и cmd2
их FD 3 открыты для трубы. cat
будет ожидать конца файла на другом конце, поэтому обычно будет выходить только тогда, когда оба cmd1
и cmd2
мертвы. И оболочка будет ждать этогоcat
команды. Вы можете видеть, что в качестве сети можно отследить завершение всех фоновых процессов (вы можете использовать его для других вещей, запускаемых в фоновом режиме, таких как &
, например, с помощью coprocs или даже команд, которые работают в фоновом режиме при условии, что они не закрывают все свои файловые дескрипторы, как это обычно делают демоны). ).
Обратите внимание, что благодаря упомянутому выше потраченному впустую процессу подоболочки, он работает, даже если cmd2
закрывает свой fd 3 (команды обычно этого не делают, но некоторые любят sudo
или ssh
делают). Будущие версии bash
могут в конечном итоге сделать оптимизацию там, как и в других оболочках. Тогда вам нужно что-то вроде:
{ { cmd1 >(sudo cmd2; exit); } 3>&1 >&4 4>&- | cat; } 4>&1
Чтобы убедиться, что еще есть дополнительный процесс оболочки с этим открытым fd 3, ожидающий эту sudo
команду.
Обратите внимание, что cat
ничего не будет читать (поскольку процессы не пишут на своем fd 3). Это просто для синхронизации. Он сделает только один read()
системный вызов, который в конце вернется ни с чем.
Вы можете избежать запуска cat
, используя подстановку команд для синхронизации канала:
{ unused=$( { cmd1 >(cmd2); } 3>&1 >&4 4>&-); } 4>&1
На этот раз это оболочка, а не cat
та, которая читает из канала, другой конец которого открыт на fd 3 of cmd1
и cmd2
. Мы используем присвоение переменной, поэтому статус выхода cmd1
доступен в$?
.
Или вы можете выполнить подстановку процесса вручную, а затем даже использовать систему, так sh
как это станет стандартным синтаксисом оболочки:
{ cmd1 /dev/fd/3 3>&1 >&4 4>&- | cmd2 4>&-; } 4>&1
хотя обратите внимание, как отмечалось ранее, что не все sh
реализации будут ждать cmd1
после cmd2
завершения (хотя это лучше, чем наоборот). Это время $?
содержит статус выхода cmd2
; хотя bash
и zsh
сделать cmd1
состояние выхода доступным в ${PIPESTATUS[0]}
и $pipestatus[1]
соответственно (см. также pipefail
параметр в нескольких оболочках, чтобы $?
можно было сообщать о сбое компонентов трубы, отличных от последнего)
Обратите внимание, что yash
есть похожие проблемы с функцией перенаправления процесса . cmd1 >(cmd2)
будет написано cmd1 /dev/fd/3 3>(cmd2)
там. Но cmd2
его не ждут, и вы тоже не можете wait
его ждать, и его pid также не доступен в $!
переменной. Вы бы использовали те же обходные пути, что и для bash
.