Да, в том же 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.