Это болезненно в оболочках, потому что встроенная функция wait
не выполняет «ожидание любого», она выполняет «ожидание всех». wait
без аргумента ожидает выхода всех дочерних процессов и возвращает 0. wait
с явным списком процессов ждет выхода всех из них и возвращает статус последнего аргумента. Чтобы дождаться нескольких потомков и получить их статус выхода, вам нужен другой подход. wait
может дать вам статус выхода, только если вы знаете, какой ребенок уже мертв.
Одним из возможных подходов является использование выделенного именованного канала для сообщения о состоянии каждого дочернего элемента. Следующий фрагмент (не проверен! )возвращает наибольший из дочерних состояний.
mkfifo status_pipe
children=0
{ child1; echo 1 $? >status_pipe; } & children=$((children+1))
{ child2; echo 2 $? >status_pipe; } & children=$((children+1))
max_status=0
while [ $children -ne 0 ]; do
read -r child status <status_pipe
children=$((children-1))
if [ $status -gt $max_status ]; then
max_status=$status
fi
done
rm status_pipe
Обратите внимание, что это будет заблокировано навсегда, если одна из подоболочек умрет, не сообщив о своем статусе. В типичных условиях этого не произойдет, но это может произойти, если подоболочка была убита вручную или если подоболочке не хватило памяти.
Если вы хотите что-то сделать, как только один из потомков выйдет из строя, замените if [ $status -gt $max_status ]; then …
на if [ $status -ne 0 ]; then …
.
Измените порядок перенаправления и сообщите bash
какой stderr должен быть перед stdin
$ >/dev/tcp/127.0.0.1/8088 2>/dev/nul
bash: connect: Connection refused
bash: /dev/tcp/127.0.0.1/8088: Connection refused
$ 2>/dev/null >/dev/tcp/127.0.0.1/8088 && echo open || echo closed
closed
$
Причина сбоя первой версии заключается в том, что перенаправления обрабатываются в порядке их появления, поэтому в >/dev/tcp/127.0.0.1/8088 2>/dev/null
когда происходит перенаправление на порт, поток stderr еще НЕ был перенаправлен. Вы можете ясно увидеть соответствующий системный вызов dup2()
для перенаправления stderr (или его отсутствие в случае сбоя команды )в диагностическом выводе с помощью утилиты strace
$ strace -e dup2,connect -f bash -c '>/dev/tcp/127.0.0.1/8088 2>/dev/null'
connect(3, {sa_family=AF_INET, sin_port=htons(8088), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 ECONNREFUSED (Connection refused)
bash: connect: Connection refused
bash: /dev/tcp/127.0.0.1/8088: Connection refused
+++ exited with 1 +++
$ strace -e dup2,connect -f bash -c '2>/dev/null >/dev/tcp/127.0.0.1/8088 '
dup2(3, 2) = 2
connect(3, {sa_family=AF_INET, sin_port=htons(8088), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 ECONNREFUSED (Connection refused)
dup2(10, 2) = 2
+++ exited with 1 +++
$