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
Я не полагаю, что это возможно со встроенной функциональностью Bash.
Вы можете получать уведомление, когда ребенок выходит:
#!/bin/sh
set -o monitor # enable script job control
trap 'echo "child died"' CHLD
Однако нет никакого очевидного способа получить статус выхода ребенка в обработчике сигналов.
Получение того дочернего состояния обычно является заданием wait
семья функций в более низких API POSIX уровня. К сожалению, поддержка Bash этого ограничена - можно ожидать один определенный дочерний процесс (и получить его статус выхода), или можно ожидать весь из них, и всегда получать 0 результатов.
то, Что кажется невозможным сделать, является эквивалентом waitpid(-1)
, какие блоки до любой дочерний процесс возвращает.
Я думаю, возможно, выполненный 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
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
Точно с этой целью я записал 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
<час> Вот что я пока придумал. Я хотел бы увидеть, как прервать команду сна, если дочерний процесс завершается, чтобы не пришлось настраивать 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