Так, 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
ви можете отримати підказки всіх фонових процесів $apids
.
es
(також з cmd1 >{cmd2}
) чекає, cmd2
як у zsh
, а також чекає cmd2
в <{cmd2}
перенаправленнях процесів.
bash
робить pid cmd2
(або точніше підкабелі, оскільки він працює cmd2
в дочірньому процесі цієї підпакеті, хоча це остання команда там) доступний $!
, але не дозволяє вам чекати на нього.
Якщо вам доведеться використовувати bash
, ви можете вирішити проблему, скориставшись командою, яка буде чекати обох команд із:
{ { cmd1 >(cmd2); } 3>&1 >&4 4>&- | cat; } 4>&1
Це робить і те, cmd1
і cmd2
їх fd 3 відкрито до труби. cat
буде чекати закінчення файлу на іншому кінці, тому, як правило, вийде лише тоді, коли вони обидва cmd1
та cmd2
мертві. І оболонка буде чекати цієї cat
команди. Ви можете бачити, що це як мережа, щоб зафіксувати завершення всіх фонових процесів (ви можете використовувати це для інших речей, розпочатих у фоновому режимі, як, наприклад &
, для копроків або навіть команд цього фону за умови, що вони не закривають усі дескриптори файлів, як, наприклад, демони ).
Зауважте, що завдяки тому, що був згаданий вище підзарядний процес, він працює, навіть якщо 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 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
.