Как дождаться завершения в bash нескольких подпроцессов и возврата кода завершения! = 0, когда любой подпроцесс заканчивается кодом! = 0?

Передать -F в grep.

483
задан codeforester 2 May 2018 в 21:47
поделиться

6 ответов

wait также (дополнительно) берет PID процесса для ожидания, и с $! Вы получаете PID последней команды, запущенной в фоне. Измените цикл для хранения PID каждого порожденного подпроцесса в массив, и затем цикла, снова ожидая на каждом PID.

# run processes and store pids in array
for i in $n_procs; do
    ./procs[${i}] &
    pids[${i}]=$!
done

# wait for all pids
for pid in ${pids[*]}; do
    wait $pid
done
439
ответ дан Cristian Ciupitu 2 May 2018 в 21:47
поделиться

Я не полагаю, что это возможно со встроенной функциональностью Bash.

Вы можете получать уведомление, когда ребенок выходит:

#!/bin/sh
set -o monitor        # enable script job control
trap 'echo "child died"' CHLD

Однако нет никакого очевидного способа получить статус выхода ребенка в обработчике сигналов.

Получение того дочернего состояния обычно является заданием wait семья функций в более низких API POSIX уровня. К сожалению, поддержка Bash этого ограничена - можно ожидать один определенный дочерний процесс (и получить его статус выхода), или можно ожидать весь из них, и всегда получать 0 результатов.

то, Что кажется невозможным сделать, является эквивалентом waitpid(-1), какие блоки до любой дочерний процесс возвращает.

7
ответ дан Alnitak 2 May 2018 в 21:47
поделиться

Я думаю, возможно, выполненный doCalculations; повторить "$?">>/tmp/acc в подоболочка , который отправляется в фон, тогда ожидание, тогда ,/tmp/acc содержал бы статусы выхода, один на строку. Я не знаю ни о каких последствиях нескольких добавления процессов в файл аккумулятора, все же.

Вот суд над этим предложением:

Файл: файл doCalcualtions

#!/bin/sh

random -e 20
sleep $?
random -e 10

: попробуйте

#!/bin/sh

rm /tmp/acc

for i in $( seq 0 20 ) 
do
        ( ./doCalculations "$i"; echo "$?" >>/tmp/acc ) &
done

wait

cat /tmp/acc | fmt
rm /tmp/acc

Вывод выполнения./попытка

5 1 9 6 8 1 2 0 9 6 5 9 6 0 0 4 9 5 5 9 8
-2
ответ дан Nietzche-jou 2 May 2018 в 21:47
поделиться

http://jeremy.zawodny.com/blog/archives/010717.html :

#!/bin/bash

FAIL=0

echo "starting"

./sleeper 2 0 &
./sleeper 2 1 &
./sleeper 3 0 &
./sleeper 2 0 &

for job in `jobs -p`
do
echo $job
    wait $job || let "FAIL+=1"
done

echo $FAIL

if [ "$FAIL" == "0" ];
then
echo "YAY!"
else
echo "FAIL! ($FAIL)"
fi
273
ответ дан HoverHell 3 May 2018 в 07:47
поделиться

Точно с этой целью я записал bash функция, вызванная :for.

Примечание : :for не только сохраняет и возвращает код выхода провальной функции, но также и завершает весь параллельный рабочий экземпляр. Который не мог бы быть необходим в этом случае.

#!/usr/bin/env bash

# Wait for pids to terminate. If one pid exits with
# a non zero exit code, send the TERM signal to all
# processes and retain that exit code
#
# usage:
# :wait 123 32
function :wait(){
    local pids=("$@")
    [ ${#pids} -eq 0 ] && return $?

    trap 'kill -INT "${pids[@]}" &>/dev/null || true; trap - INT' INT
    trap 'kill -TERM "${pids[@]}" &>/dev/null || true; trap - RETURN TERM' RETURN TERM

    for pid in "${pids[@]}"; do
        wait "${pid}" || return $?
    done

    trap - INT RETURN TERM
}

# Run a function in parallel for each argument.
# Stop all instances if one exits with a non zero
# exit code
#
# usage:
# :for func 1 2 3
#
# env:
# FOR_PARALLEL: Max functions running in parallel
function :for(){
    local f="${1}" && shift

    local i=0
    local pids=()
    for arg in "$@"; do
        ( ${f} "${arg}" ) &
        pids+=("$!")
        if [ ! -z ${FOR_PARALLEL+x} ]; then
            (( i=(i+1)%${FOR_PARALLEL} ))
            if (( i==0 )) ;then
                :wait "${pids[@]}" || return $?
                pids=()
            fi
        fi
    done && [ ${#pids} -eq 0 ] || :wait "${pids[@]}" || return $?
}

использование

for.sh:

#!/usr/bin/env bash
set -e

# import :for from gist: https://gist.github.com/Enteee/c8c11d46a95568be4d331ba58a702b62#file-for
# if you don't like curl imports, source the actual file here.
source <(curl -Ls https://gist.githubusercontent.com/Enteee/c8c11d46a95568be4d331ba58a702b62/raw/)

msg="You should see this three times"

:(){
  i="${1}" && shift

  echo "${msg}"

  sleep 1
  if   [ "$i" == "1" ]; then sleep 1
  elif [ "$i" == "2" ]; then false
  elif [ "$i" == "3" ]; then
    sleep 3
    echo "You should never see this"
  fi
} && :for : 1 2 3 || exit $?

echo "You should never see this"
$ ./for.sh; echo $?
You should see this three times
You should see this three times
You should see this three times
1
<час>

Ссылки

0
ответ дан Ente 4 November 2019 в 08:11
поделиться
  • 1
    Исключения входа @roygbiv и транзакции правильно могут минимизировать потребность в отладке. тесно связанный используемый. – Chris Ballance 21 December 2009 в 22:23

Вот что я пока придумал. Я хотел бы увидеть, как прервать команду сна, если дочерний процесс завершается, чтобы не пришлось настраивать WAITALL_DELAY на свое использование.

waitall() { # PID...
  ## Wait for children to exit and indicate whether all exited with 0 status.
  local errors=0
  while :; do
    debug "Processes remaining: $*"
    for pid in "$@"; do
      shift
      if kill -0 "$pid" 2>/dev/null; then
        debug "$pid is still alive."
        set -- "$@" "$pid"
      elif wait "$pid"; then
        debug "$pid exited with zero exit status."
      else
        debug "$pid exited with non-zero exit status."
        ((++errors))
      fi
    done
    (("$#" > 0)) || break
    # TODO: how to interrupt this sleep when a child terminates?
    sleep ${WAITALL_DELAY:-1}
   done
  ((errors == 0))
}

debug() { echo "DEBUG: $*" >&2; }

pids=""
for t in 3 5 4; do 
  sleep "$t" &
  pids="$pids $!"
done
waitall $pids
39
ответ дан 22 November 2019 в 22:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: