여러 하위 프로세스가 완료될 때까지 bash에서 기다렸다가 하위 프로세스가 코드 !=0으로 끝날 때 종료 코드 !=0을 반환하는 방법?
된 여러 종료될 bash를 !=0
가 코드 「」로 .!=0
간단한 스크립트:
#!/bin/bash
for i in `seq 0 9`; do
doCalculations $i &
done
wait
상태 "10"이 .0
).help wait
와 종료 하도록 이 해야 1
가 코드 「」로 종료했을 .!=0
서브프로세스의 PID를 수집하여 순서대로 대기하고 종료 상태를 합산하는 것보다 더 좋은 솔루션이 있습니까?
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
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
간단한 .wait
.
몇 가지 프로세스를 실행합니다.
$ sleep 10 &
$ sleep 10 &
$ sleep 20 &
$ sleep 20 &
함께.wait
★★★★★★★★★★★★★★★★★★:
$ wait < <(jobs -p)
그냥 ★★★★★★★★★★★★★★★★★.wait
(어느 쪽인가 하면) (어느 쪽인가 하면)
백그라운드에서 모든 작업이 완료될 때까지 기다립니다.
경우,-n
옵션이 지정되면 다음 작업이 종료될 때까지 기다렸다가 종료 상태를 반환합니다.
help wait
★★★★★★★★★★★★★★★★★」help jobs
를 참조해 주세요.
단, 마지막 ID 상태에만 반환되므로 각 서브프로세스의 상태를 확인하고 변수에 저장해야 합니다.
또는 계산 함수를 사용하여 실패 시(빈 파일 또는 실패 로그가 있는 파일)를 만든 다음 해당 파일이 있는 경우 확인합니다.
$ sleep 20 && true || tee fail &
$ sleep 20 && false || tee fail &
$ wait < <(jobs -p)
$ test -f fail && echo Calculation failed.
단순하게 하면 어떨까요?
#!/bin/bash
pids=""
for i in `seq 0 9`; do
doCalculations $i &
pids="$pids $!"
done
wait $pids
...code continued here ...
업데이트:
복수의 코멘트가 지적한 바와 같이 위의 내용은 모든 프로세스가 완료될 때까지 기다린 후 계속 진행합니다.단, 이들 중 하나가 실패해도 종료 및 실패하지 않습니다.@Bryan, @SamBrightman 등이 제안하는 다음 변경과 관련이 있습니다.
#!/bin/bash
pids=""
RESULT=0
for i in `seq 0 9`; do
doCalculations $i &
pids="$pids $!"
done
for pid in $pids; do
wait $pid || let "RESULT=1"
done
if [ "$RESULT" == "1" ];
then
exit 1
fi
...code continued here ...
GNU Parallel 이 인스톨 되어 있는 경우는, 다음의 조작을 실행할 수 있습니다.
# If doCalculations is a function
export -f doCalculations
seq 0 9 | parallel doCalculations {}
GNU Parallel은 종료 코드를 제공합니다.
0 - 모든 작업이 오류 없이 실행되었습니다.
1-253 - 일부 작업이 실패했습니다.종료 상태는 실패한 작업 수를 제공합니다.
254 - 253개 이상의 작업이 실패했습니다.
255 - 기타 오류.
자세한 내용은 소개 비디오를 참조하십시오.http://pi.dk/1
내가 지금까지 생각해낸 것은 이렇다.때가 sleep 명령을 튜닝하지 .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
평행하게 하려면...
for i in $(whatever_list) ; do
do_something $i
done
이렇게 번역하면...
for i in $(whatever_list) ; do echo $i ; done | ## execute in parallel...
(
export -f do_something ## export functions (if needed)
export PATH ## export any variables that are required
xargs -I{} --max-procs 0 bash -c ' ## process in batches...
{
echo "processing {}" ## optional
do_something {}
}'
)
- 한 프로세스에서 오류가 발생하더라도 다른 프로세스를 중단하지는 않지만 시퀀스 전체에서 0이 아닌 종료 코드가 발생합니다.
- 함수 및 변수 내보내기는 어떤 경우에도 필요하거나 필요하지 않을 수 있습니다.
- 설정할 수 .
--max-procs
원하는가에0
"한 번에"를 의미합니다. - GNU Parallel을 사용하면 다음과 같은 추가 기능을 이용할 수 있습니다.
xargs
되는 것은 .-- 、 、 、 、 -- -- -- -- -- -- -- -- -- -- 。 for
꼭 한 것은 .echo $i
으로는 '는 것'입니다.$(whatever_list
합니다.for
키워드를 지정하면 상황을 알기 쉬워집니다.- Bash 문자열 처리는 혼란스러울 수 있습니다.- 사소한 스크립트는 작은 따옴표를 사용하는 것이 가장 좋습니다.
- Bash 병렬화에 대한 보다 직접적인 접근 방식과는 달리 전체 작업을 쉽게 중단할 수 있습니다(^C 또는 이와 유사).
여기 간단한 작업 예가 있습니다.
for i in {0..5} ; do echo $i ; done |xargs -I{} --max-procs 2 bash -c '
{
echo sleep {}
sleep 2s
}'
이것은 제가 사용하는 것입니다.
#wait for jobs
for job in `jobs -p`; do wait ${job}; done
이것은 @Luca Tettamanti의 가장 높은 평가를 받은 답변을 완전히 실행 가능한 예로 확장한 것입니다.
그 대답에 나는 의아해 했다.
입니까?
n_procs
, , , , , , , , , , , , , ? 입니까?procs
, , , , , , , , , , , , , ?이러한 변수에 대한 정의를 추가하여 실행할 수 있도록 이 답변을 업데이트해 주실 수 있습니까?츠키노
...또,
- 서브프로세스가 완료되었을 때(이 질문의 핵심이 되는 부분)에서 반품 코드를 취득하려면 어떻게 해야 합니까?
어쨌든, 제가 알아냈어요. 여기 완전히 실행 가능한 예가 있어요.
주의:
$!
는 마지막으로 실행된 서브프로세스의 PID(프로세스 ID)를 취득하는 방법입니다.- 를 " "와 실행"
&
뒤에 그 after , 치치 after after after after after aftercmd &
예를 들어 메인 프로세스와 병렬 하위 프로세스로 백그라운드에서 실행됩니다. myarray=()
바쉬- 더 ,
wait
" " 참조help wait
Job Control의 빌트인에 관한 공식 Bash 사용자 매뉴얼도 참조하십시오.wait
★★★★★★★★★★★★★★★★★」jobs
, 여기: https://www.gnu.org/software/bash/manual/html_node/Job-Control-Builtins.html#index-wait
실행 가능한 전체 프로그램: 모든 프로세스가 종료될 때까지 기다립니다.
multi_process_program.쉿 (eRCaGuy_hello_world repo에서):
#!/usr/bin/env bash
# This is a special sleep function which returns the number of seconds slept as
# the "error code" or return code" so that we can easily see that we are in
# fact actually obtaining the return code of each process as it finishes.
my_sleep() {
seconds_to_sleep="$1"
sleep "$seconds_to_sleep"
return "$seconds_to_sleep"
}
# Create an array of whatever commands you want to run as subprocesses
procs=() # bash array
procs+=("my_sleep 5")
procs+=("my_sleep 2")
procs+=("my_sleep 3")
procs+=("my_sleep 4")
num_procs=${#procs[@]} # number of processes
echo "num_procs = $num_procs"
# run commands as subprocesses and store pids in an array
pids=() # bash array
for (( i=0; i<"$num_procs"; i++ )); do
echo "cmd = ${procs[$i]}"
${procs[$i]} & # run the cmd as a subprocess
# store pid of last subprocess started; see:
# https://unix.stackexchange.com/a/30371/114401
pids+=("$!")
echo " pid = ${pids[$i]}"
done
# OPTION 1 (comment this option out if using Option 2 below): wait for all pids
for pid in "${pids[@]}"; do
wait "$pid"
return_code="$?"
echo "PID = $pid; return_code = $return_code"
done
echo "All $num_procs processes have ended."
의 을 실행 합니다.chmod +x multi_process_program.sh
뭇매를 맞다
time ./multi_process_program.sh
「」의 출력을 .time
명령어를 실행하면 실행하는데 5.084초가 걸렸다는 것을 알 수 있습니다.또, 각 서브 프로세스로부터 리턴 코드를 정상적으로 취득할 수 있었습니다.
eRCaGuy_hello_world/bash$ time ./multi_process_program.sh num_procs = 4 cmd = my_sleep 5 pid = 21694 cmd = my_sleep 2 pid = 21695 cmd = my_sleep 3 pid = 21697 cmd = my_sleep 4 pid = 21699 PID = 21694; return_code = 5 PID = 21695; return_code = 2 PID = 21697; return_code = 3 PID = 21699; return_code = 4 All 4 processes have ended. PID 21694 is done; return_code = 5; 3 PIDs remaining. PID 21695 is done; return_code = 2; 2 PIDs remaining. PID 21697 is done; return_code = 3; 1 PIDs remaining. PID 21699 is done; return_code = 4; 0 PIDs remaining. real 0m5.084s user 0m0.025s sys 0m0.061s
계속 진행: 각 프로세스가 언제 종료되는지 실시간으로 확인
종료되었을 을 하고 는, 합니다.while
루프에서 각 프로세스가 언제 종료되는지 확인한 후 원하는 작업을 수행합니다.
위의 코드 "OPTION 1" 블록을 코멘트하고 대신 이 "OPTION 2" 블록으로 바꾸면 됩니다.
# OR OPTION 2 (comment out Option 1 above if using Option 2): poll to detect
# when each process terminates, and print out when each process finishes!
while true; do
for i in "${!pids[@]}"; do
pid="${pids[$i]}"
# echo "pid = $pid" # debugging
# See if PID is still running; see my answer here:
# https://stackoverflow.com/a/71134379/4561887
ps --pid "$pid" > /dev/null
if [ "$?" -ne 0 ]; then
# PID doesn't exist anymore, meaning it terminated
# 1st, read its return code
wait "$pid"
return_code="$?"
# 2nd, remove this PID from the `pids` array by `unset`ting the
# element at this index; NB: due to how bash arrays work, this does
# NOT actually remove this element from the array. Rather, it
# removes its index from the `"${!pids[@]}"` list of indices,
# adjusts the array count(`"${#pids[@]}"`) accordingly, and it sets
# the value at this index to either a null value of some sort, or
# an empty string (I'm not exactly sure).
unset "pids[$i]"
num_pids="${#pids[@]}"
echo "PID $pid is done; return_code = $return_code;" \
"$num_pids PIDs remaining."
fi
done
# exit the while loop if the `pids` array is empty
if [ "${#pids[@]}" -eq 0 ]; then
break
fi
# Do some small sleep here to keep your polling loop from sucking up
# 100% of one of your CPUs unnecessarily. Sleeping allows other processes
# to run during this time.
sleep 0.1
done
옵션 1이 코멘트 아웃되어 옵션 2가 사용 중인 풀 프로그램의 실행 및 출력 예:
eRCaGuy_hello_world/bash$ ./multi_process_program.sh num_procs = 4 cmd = my_sleep 5 pid = 22275 cmd = my_sleep 2 pid = 22276 cmd = my_sleep 3 pid = 22277 cmd = my_sleep 4 pid = 22280 PID 22276 is done; return_code = 2; 3 PIDs remaining. PID 22277 is done; return_code = 3; 2 PIDs remaining. PID 22280 is done; return_code = 4; 1 PIDs remaining. PID 22275 is done; return_code = 5; 0 PIDs remaining.
의 ★★PID XXXXX is done
라인은 프로세스가 종료된 직후에 라이브로 출력됩니다. 이 에서는 '', '아예', '아예', '아예', '아예', '아예', '아예', '아예', '아예', '아예', '아예', '아예', '아예', '아예', 'sleep 5
(PID)22275
이 경우)가 먼저 실행되어 마지막으로 종료되었으며 각 프로세스가 종료된 직후에 성공적으로 검출되었습니다.옵션 1과 같이 각 리턴 코드도 검출했습니다.
기타 참고 자료:
*****+ [매우 도움이 되는] 백그라운드 프로세스의 종료 코드를 가져옵니다.이 답변은 다음과 같은 주요 원칙을 가르쳐 주었습니다(강조 추가).
wait <n>
는 PID가 설정된 프로세스가 완료될 때까지 대기한 후(프로세스가 완료될 때까지 차단되므로 프로세스가 완료되었음을 확인할 때까지 호출하지 않을 수 있습니다), 완료된 프로세스의 종료 코드를 반환합니다.즉, 프로세스가 완료된 후에도 반품 코드를 받기 위해 전화를 할 수 있다는 것을 알 수 있었습니다.
Bash 배열에서 요소를 제거합니다. Bash 배열의 요소는 실제로 삭제되는 것이 아니라 "설정 해제"됩니다.그 의미에 대해서는, 상기의 코드의 코멘트를 참조해 주세요.
파일 사용
true
bash에서 무한 while 루프를 작성하려면:https://www.cyberciti.biz/faq/bash-infinite-loop/
여기 좋은 예가 많이 있는데, 내 것도 같이 넣고 싶었거든.
#! /bin/bash
items="1 2 3 4 5 6"
pids=""
for item in $items; do
sleep $item &
pids+="$! "
done
for pid in $pids; do
wait $pid
if [ $? -eq 0 ]; then
echo "SUCCESS - Job $pid exited with a status of $?"
else
echo "FAILED - Job $pid exited with a status of $?"
fi
done
서버/서비스를 동시에 시작/정지하고 각 종료 상태를 확인합니다.나한테는 잘 먹힌다.이게 도움이 됐으면 좋겠네요!
Bash의 내장 기능으로는 불가능할 것 같습니다.
자녀가 종료할 때 알림을 받을 수 있습니다.
#!/bin/sh
set -o monitor # enable script job control
trap 'echo "child died"' CHLD
하지만 신호 핸들러에서 아이의 출구 상태를 확인할 수 있는 명백한 방법은 없습니다.
를 얻는 '아이의 일'입니다.wait
하위 수준 POSIX API의 함수 패밀리.안타깝게도 Bash의 지원은 제한되어 있습니다.한 개의 특정 하위 프로세스를 기다리거나(및 종료 상태를 가져오거나) 모든 프로세스를 기다리면 항상 0의 결과를 얻을 수 있습니다.
불가능하다고 생각되는 것은 에 상당한다.waitpid(-1)
하위 프로세스가 반환될 때까지 차단합니다.
다음 코드는 모든 계산이 완료될 때까지 대기하고 doCalculation 중 하나가 실패하면 종료 상태 1을 반환합니다.
#!/bin/bash
for i in $(seq 0 9); do
(doCalculations $i >&2 & wait %1; echo $?) &
done | grep -qv 0 && exit 1
이 버전은 여러 PID에서 작동하며, 실행이 너무 오래 걸리는 경우 경고를 기록하고, 실행이 지정된 값보다 오래 걸리는 경우 하위 프로세스를 중지합니다.
[편집] 새로운 Wait For Task Completion 구현 Exec Tasks를 https://github.com/deajan/ofunctions에 업로드했습니다.Wait For Task Completion [ / EDIT ]호환 레이어도 있습니다.
function WaitForTaskCompletion {
local pids="${1}" # pids to wait for, separated by semi-colon
local soft_max_time="${2}" # If execution takes longer than $soft_max_time seconds, will log a warning, unless $soft_max_time equals 0.
local hard_max_time="${3}" # If execution takes longer than $hard_max_time seconds, will stop execution, unless $hard_max_time equals 0.
local caller_name="${4}" # Who called this function
local exit_on_error="${5:-false}" # Should the function exit program on subprocess errors
Logger "${FUNCNAME[0]} called by [$caller_name]."
local soft_alert=0 # Does a soft alert need to be triggered, if yes, send an alert once
local log_ttime=0 # local time instance for comparaison
local seconds_begin=$SECONDS # Seconds since the beginning of the script
local exec_time=0 # Seconds since the beginning of this function
local retval=0 # return value of monitored pid process
local errorcount=0 # Number of pids that finished with errors
local pidCount # number of given pids
IFS=';' read -a pidsArray <<< "$pids"
pidCount=${#pidsArray[@]}
while [ ${#pidsArray[@]} -gt 0 ]; do
newPidsArray=()
for pid in "${pidsArray[@]}"; do
if kill -0 $pid > /dev/null 2>&1; then
newPidsArray+=($pid)
else
wait $pid
result=$?
if [ $result -ne 0 ]; then
errorcount=$((errorcount+1))
Logger "${FUNCNAME[0]} called by [$caller_name] finished monitoring [$pid] with exitcode [$result]."
fi
fi
done
## Log a standby message every hour
exec_time=$(($SECONDS - $seconds_begin))
if [ $((($exec_time + 1) % 3600)) -eq 0 ]; then
if [ $log_ttime -ne $exec_time ]; then
log_ttime=$exec_time
Logger "Current tasks still running with pids [${pidsArray[@]}]."
fi
fi
if [ $exec_time -gt $soft_max_time ]; then
if [ $soft_alert -eq 0 ] && [ $soft_max_time -ne 0 ]; then
Logger "Max soft execution time exceeded for task [$caller_name] with pids [${pidsArray[@]}]."
soft_alert=1
SendAlert
fi
if [ $exec_time -gt $hard_max_time ] && [ $hard_max_time -ne 0 ]; then
Logger "Max hard execution time exceeded for task [$caller_name] with pids [${pidsArray[@]}]. Stopping task execution."
kill -SIGTERM $pid
if [ $? == 0 ]; then
Logger "Task stopped successfully"
else
errrorcount=$((errorcount+1))
fi
fi
fi
pidsArray=("${newPidsArray[@]}")
sleep 1
done
Logger "${FUNCNAME[0]} ended for [$caller_name] using [$pidCount] subprocesses with [$errorcount] errors."
if [ $exit_on_error == true ] && [ $errorcount -gt 0 ]; then
Logger "Stopping execution."
exit 1337
else
return $errorcount
fi
}
# Just a plain stupid logging function to be replaced by yours
function Logger {
local value="${1}"
echo $value
}
예를 들어, 세 프로세스가 모두 완료될 때까지 기다린 후, 실행 시간이 5초 미만이면 경고를 기록하고, 실행 시간이 120초 미만이면 모든 프로세스를 중지합니다.실패 시 프로그램을 종료하지 마십시오.
function something {
sleep 10 &
pids="$!"
sleep 12 &
pids="$pids;$!"
sleep 9 &
pids="$pids;$!"
WaitForTaskCompletion $pids 5 120 ${FUNCNAME[0]} false
}
# Launch the function
someting
bash 4.2 이후를 사용할 수 있는 경우 다음 사항이 유용할 수 있습니다.관련 배열을 사용하여 작업 이름과 "코드"뿐만 아니라 작업 이름과 해당 피드를 저장합니다.또한 간단한 환율 제한 방법도 포함되어 있어 작업에 CPU 또는 I/O 시간이 많이 걸리고 동시 작업 수를 제한하려는 경우 유용합니다.
스크립트는 첫 번째 루프에서 모든 작업을 시작하고 두 번째 루프에서 결과를 소비합니다.
이것은 단순한 경우엔 조금 과잉 살상이지만 꽤 깔끔한 것이 가능하다.예를 들어 각 작업의 오류 메시지를 다른 연관 배열에 저장하고 모든 작업이 해결된 후 인쇄할 수 있습니다.
#! /bin/bash
main () {
local -A pids=()
local -A tasks=([task1]="echo 1"
[task2]="echo 2"
[task3]="echo 3"
[task4]="false"
[task5]="echo 5"
[task6]="false")
local max_concurrent_tasks=2
for key in "${!tasks[@]}"; do
while [ $(jobs 2>&1 | grep -c Running) -ge "$max_concurrent_tasks" ]; do
sleep 1 # gnu sleep allows floating point here...
done
${tasks[$key]} &
pids+=(["$key"]="$!")
done
errors=0
for key in "${!tasks[@]}"; do
pid=${pids[$key]}
local cur_ret=0
if [ -z "$pid" ]; then
echo "No Job ID known for the $key process" # should never happen
cur_ret=1
else
wait $pid
cur_ret=$?
fi
if [ "$cur_ret" -ne 0 ]; then
errors=$(($errors + 1))
echo "$key (${tasks[$key]}) failed."
fi
done
return $errors
}
main
나는 이것을 시도해 보고 여기에 있는 다른 예에서 가장 좋은 부분들을 모두 결합했다.는 " " 를 합니다.checkpids
백그라운드프로세스가 종료되었을 때 기능하고 폴링에 의존하지 않고 종료 상태를 출력합니다.
#!/bin/bash
set -o monitor
sleep 2 &
sleep 4 && exit 1 &
sleep 6 &
pids=`jobs -p`
checkpids() {
for pid in $pids; do
if kill -0 $pid 2>/dev/null; then
echo $pid is still alive.
elif wait $pid; then
echo $pid exited with zero exit status.
else
echo $pid exited with non-zero exit status.
fi
done
echo
}
trap checkpids CHLD
wait
#!/bin/bash
set -m
for i in `seq 0 9`; do
doCalculations $i &
done
while fg; do true; done
set -m
'fg & bg에서 fg & 를 사용할 수 .fg
하는 것 에, 그 에서 선행하는 한 종료 를 가집니다.while fg
합니다.fg
a exit " 0 " 。
불행히도 백그라운드에서 프로세스가 종료 상태가 0이 아닌 상태로 종료되는 경우는 처리되지 않습니다.(루프는 즉시 종료되지 않습니다.이전 프로세스가 완료될 때까지 기다립니다.)
모든 작업을 기다린 후 마지막으로 실패한 작업의 종료 코드를 반환합니다.위의 솔루션과 달리 이 방법에서는 pid 저장이나 스크립트의 내부 루프를 변경할 필요가 없습니다.조금만 더 버티고 기다려
function wait_ex {
# this waits for all jobs and returns the exit code of the last failing job
ecode=0
while true; do
[ -z "$(jobs)" ] && break
wait -n
err="$?"
[ "$err" != "0" ] && ecode="$err"
done
return $ecode
}
편집: 존재하지 않는 명령을 실행하는 스크립트에 의해 이 오류가 속아 넘어갈 수 있는 오류를 수정했습니다.
셸에서 나온 결과를 파일에 저장하기만 하면 됩니다.
#!/bin/bash
tmp=/tmp/results
: > $tmp #clean the file
for i in `seq 0 9`; do
(doCalculations $i; echo $i:$?>>$tmp)&
done #iterate
wait #wait until all ready
sort $tmp | grep -v ':0' #... handle as required
나는 단지 백그라운드와 프로세스를 병렬화하기 위해 스크립트를 수정하고 있었을 뿐이다.
Solaris에서 (bash와 ksh를 모두 사용하여) 몇 가지 실험을 해봤더니, 'wait'가 0이 아니면 종료 상태를 출력하거나 PID 인수가 제공되지 않은 경우 0이 아닌 종료를 반환하는 작업 목록을 출력하는 것을 발견했습니다.예.
배쉬:
$ sleep 20 && exit 1 &
$ sleep 10 && exit 2 &
$ wait
[1]- Exit 2 sleep 20 && exit 2
[2]+ Exit 1 sleep 10 && exit 1
Ksh:
$ sleep 20 && exit 1 &
$ sleep 10 && exit 2 &
$ wait
[1]+ Done(2) sleep 20 && exit 2
[2]+ Done(1) sleep 10 && exit 1
이 출력은 stderr에 기록되므로 OP의 예에 대한 간단한 해결책은 다음과 같습니다.
#!/bin/bash
trap "rm -f /tmp/x.$$" EXIT
for i in `seq 0 9`; do
doCalculations $i &
done
wait 2> /tmp/x.$$
if [ `wc -l /tmp/x.$$` -gt 0 ] ; then
exit 1
fi
그 동안:
wait 2> >(wc -l)
는 카운트를 반환하지만 tmp 파일은 반환하지 않습니다.이것은 다음과 같은 방법으로도 사용할 수 있습니다.
wait 2> >(if [ `wc -l` -gt 0 ] ; then echo "ERROR"; fi)
그러나 이것은 tmp 파일 IMO보다 그다지 유용하지 않습니다.tmp 파일을 피하는 동시에 서브셸에서 "wait"를 실행하는 것을 피할 수 있는 유용한 방법을 찾을 수 없었습니다.
셸의 , 이 셸의 자식은 아닙니다.wait $PID
동작하지 않습니다.신음
while [ -e /proc/$PID ]; do sleep 0.1 ; done
이는 procfs의 존재에 의존하며, procfs는 사용할 수 없습니다(예를 들어 Mac은 procfs를 제공하지 않습니다).따라서 휴대성을 위해 대신 다음을 사용할 수 있습니다.
while ps -p $PID >/dev/null ; do sleep 0.1 ; done
여기에는 이미 많은 답변이 있습니다만, 어레이를 사용하는 것을 추천하는 사람이 없는 것 같아서 놀랐습니다.그래서 제가 한 일은 이렇습니다. 이것은 미래에 몇몇 사람들에게 유용할 것입니다.
n=10 # run 10 jobs
c=0
PIDS=()
while true
my_function_or_command &
PID=$!
echo "Launched job as PID=$PID"
PIDS+=($PID)
(( c+=1 ))
# required to prevent any exit due to error
# caused by additional commands run which you
# may add when modifying this example
true
do
if (( c < n ))
then
continue
else
break
fi
done
# collect launched jobs
for pid in "${PIDS[@]}"
do
wait $pid || echo "failed job PID=$pid"
done
이 방법은 @HoverHell의 답변보다 더 낫지는 않더라도 똑같이 좋습니다.
#!/usr/bin/env bash
set -m # allow for job control
EXIT_CODE=0; # exit code of overall script
function foo() {
echo "CHLD exit code is $1"
echo "CHLD pid is $2"
echo $(jobs -l)
for job in `jobs -p`; do
echo "PID => ${job}"
wait ${job} || echo "At least one test failed with exit code => $?" ; EXIT_CODE=1
done
}
trap 'foo $? $$' CHLD
DIRN=$(dirname "$0");
commands=(
"{ echo "foo" && exit 4; }"
"{ echo "bar" && exit 3; }"
"{ echo "baz" && exit 5; }"
)
clen=`expr "${#commands[@]}" - 1` # get length of commands - 1
for i in `seq 0 "$clen"`; do
(echo "${commands[$i]}" | bash) & # run the command via bash in subshell
echo "$i ith command has been issued as a background job"
done
# wait for all to finish
wait;
echo "EXIT_CODE => $EXIT_CODE"
exit "$EXIT_CODE"
# end
물론 이 스크립트는 NPM 프로젝트에서 영속화했습니다.이를 통해 bash 명령어를 병렬로 실행할 수 있어 테스트에 도움이 됩니다.
https://github.com/ORESoftware/generic-subshell
로 제가 .bash
function함수라고 하는 :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
레퍼런스
set -e
fail () {
touch .failure
}
expect () {
wait
if [ -f .failure ]; then
rm -f .failure
exit 1
fi
}
sleep 2 || fail &
sleep 2 && false || fail &
sleep 2 || fail
expect
set -e
위는 실패 시 스크립트를 중지합니다.
expect
1
이치노
프로세스를 대기하기 전에 프로세스가 완료되는 경우가 있습니다.이미 완료된 프로세스에 대해 wait를 트리거하면 pid는 이 셸의 자녀가 아닙니다.와 같은 오류가 트리거됩니다.이러한 경우를 피하기 위해 다음 함수를 사용하여 프로세스가 완료되었는지 여부를 확인할 수 있습니다.
isProcessComplete(){
PID=$1
while [ -e /proc/$PID ]
do
echo "Process: $PID is still running"
sleep 5
done
echo "Process $PID has finished"
}
나는 하마터면 이용의 덫에 빠질 뻔했다.jobs -p
PID pid 집 pid 니 、아래 스크립트에 나타나 있듯이 자녀가 이미 종료된 경우에는 이 기능이 작동하지 않습니다. '불러서'라고 것이었다.wait -n
N번, 여기서 N은 제가 가진 아이의 수이고, 제가 결정적으로 알고 있는 숫자입니다.
#!/usr/bin/env bash
sleeper() {
echo "Sleeper $1"
sleep $2
echo "Exiting $1"
return $3
}
start_sleepers() {
sleeper 1 1 0 &
sleeper 2 2 $1 &
sleeper 3 5 0 &
sleeper 4 6 0 &
sleep 4
}
echo "Using jobs"
start_sleepers 1
pids=( $(jobs -p) )
echo "PIDS: ${pids[*]}"
for pid in "${pids[@]}"; do
wait "$pid"
echo "Exit code $?"
done
echo "Clearing other children"
wait -n; echo "Exit code $?"
wait -n; echo "Exit code $?"
echo "Waiting for N processes"
start_sleepers 2
for ignored in $(seq 1 4); do
wait -n
echo "Exit code $?"
done
출력:
Using jobs
Sleeper 1
Sleeper 2
Sleeper 3
Sleeper 4
Exiting 1
Exiting 2
PIDS: 56496 56497
Exiting 3
Exit code 0
Exiting 4
Exit code 0
Clearing other children
Exit code 0
Exit code 1
Waiting for N processes
Sleeper 1
Sleeper 2
Sleeper 3
Sleeper 4
Exiting 1
Exiting 2
Exit code 0
Exit code 2
Exiting 3
Exit code 0
Exiting 4
Exit code 0
Bash 5.1부터는 여러 백그라운드 작업의 결과를 기다리고 처리할 수 있는 새로운 방법이 추가되었습니다.wait -p
:
#!/usr/bin/env bash
# Spawn background jobs
for ((i=0; i < 10; i++)); do
secs=$((RANDOM % 10)); code=$((RANDOM % 256))
(sleep ${secs}; exit ${code}) &
echo "Started background job (pid: $!, sleep: ${secs}, code: ${code})"
done
# Wait for background jobs, print individual results, determine overall result
result=0
while true; do
wait -n -p pid; code=$?
[[ -z "${pid}" ]] && break
echo "Background job ${pid} finished with code ${code}"
(( ${code} != 0 )) && result=1
done
# Return overall result
exit ${result}
최근에 이것을 사용했습니다(Alnitak 덕분에).
#!/bin/bash
# activate child monitoring
set -o monitor
# locking subprocess
(while true; do sleep 0.001; done) &
pid=$!
# count, and kill when all done
c=0
function kill_on_count() {
# you could kill on whatever criterion you wish for
# I just counted to simulate bash's wait with no args
[ $c -eq 9 ] && kill $pid
c=$((c+1))
echo -n '.' # async feedback (but you don't know which one)
}
trap "kill_on_count" CHLD
function save_status() {
local i=$1;
local rc=$2;
# do whatever, and here you know which one stopped
# but remember, you're called from a subshell
# so vars have their values at fork time
}
# care must be taken not to spawn more than one child per loop
# e.g don't use `seq 0 9` here!
for i in {0..9}; do
(doCalculations $i; save_status $i $?) &
done
# wait for locking subprocess to be killed
wait $pid
echo
여기서 쉽게 추정할 수 있으며 트리거(파일 터치, 신호 전송) 및 카운트 기준(터치된 파일 수 등)을 변경하여 해당 트리거에 응답할 수 있습니다.또는 0이 아닌 임의의 rc를 원하는 경우 save_status에서 잠금을 삭제합니다.
CHLD 신호가 동시에 도달하면 신호가 손실될 수 있으므로 CHLD 신호가 트랩되지 않을 수 있습니다.
#!/bin/bash
trap 'rm -f $tmpfile' EXIT
tmpfile=$(mktemp)
doCalculations() {
echo start job $i...
sleep $((RANDOM % 5))
echo ...end job $i
exit $((RANDOM % 10))
}
number_of_jobs=10
for i in $( seq 1 $number_of_jobs )
do
( trap "echo job$i : exit value : \$? >> $tmpfile" EXIT; doCalculations ) &
done
wait
i=0
while read res; do
echo "$res"
let i++
done < "$tmpfile"
echo $i jobs done !!!
몇 개의 서브프로세스를 대기하고 상태 코드가 0이 아닌 상태에서 이들 중 하나가 종료되었을 때 종료하는 솔루션은 'wait - n'을 사용하는 것입니다.
#!/bin/bash
wait_for_pids()
{
for (( i = 1; i <= $#; i++ )) do
wait -n $@
status=$?
echo "received status: "$status
if [ $status -ne 0 ] && [ $status -ne 127 ]; then
exit 1
fi
done
}
sleep_for_10()
{
sleep 10
exit 10
}
sleep_for_20()
{
sleep 20
}
sleep_for_10 &
pid1=$!
sleep_for_20 &
pid2=$!
wait_for_pids $pid2 $pid1
상태 코드 'display'는 존재하지 않는 프로세스에 대한 것으로, 자녀가 종료되었을 수 있습니다.
언급URL : https://stackoverflow.com/questions/356100/how-to-wait-in-bash-for-several-subprocesses-to-finish-and-return-exit-code-0
'programing' 카테고리의 다른 글
변수를 소스 없이 Bash 스크립트에서 환경으로 내보낼 수 있습니까? (0) | 2023.04.14 |
---|---|
PowerShell의 *Nix 'which' 명령어와 동등합니까? (0) | 2023.04.14 |
UITextField 암호 숨기기 (0) | 2023.04.14 |
'git pull'과 'git fetch'의 차이점은 무엇입니까? (0) | 2023.04.09 |
WPF 코드 배후에 의한 리소스 액세스 (0) | 2023.04.09 |