programing

여러 하위 프로세스가 완료될 때까지 bash에서 기다렸다가 하위 프로세스가 코드 !=0으로 끝날 때 종료 코드 !=0을 반환하는 방법?

elseif 2023. 4. 14. 21:16

여러 하위 프로세스가 완료될 때까지 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 , , , , , , , , , , , , , ?이러한 변수에 대한 정의를 추가하여 실행할 수 있도록 이 답변을 업데이트해 주실 수 있습니까?츠키노

...또,

  • 서브프로세스가 완료되었을 때(이 질문의 핵심이 되는 부분)에서 반품 코드를 취득하려면 어떻게 해야 합니까?

어쨌든, 제가 알아냈어요. 여기 완전히 실행 가능한 예가 있어요.

주의:

  1. $!는 마지막으로 실행된 서브프로세스의 PID(프로세스 ID)를 취득하는 방법입니다.
  2. 를 " "와 실행"&뒤에 그 after , 치치 after after after after after aftercmd &예를 들어 메인 프로세스와 병렬 하위 프로세스로 백그라운드에서 실행됩니다.
  3. myarray=()바쉬
  4. 더 ,wait " " 참조help waitJob 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과 같이 각 리턴 코드도 검출했습니다.

기타 참고 자료:

  1. *****+ [매우 도움이 되는] 백그라운드 프로세스의 종료 코드를 가져옵니다.이 답변은 다음과 같은 주요 원칙을 가르쳐 주었습니다(강조 추가).

    wait <n>는 PID가 설정된 프로세스가 완료될 때까지 대기한 후(프로세스가 완료될 때까지 차단되므로 프로세스가 완료되었음을 확인할 때까지 호출하지 않을 수 있습니다), 완료된 프로세스의 종료 코드를 반환합니다.

    즉, 프로세스가 완료된 후에도 반품 코드를 받기 위해 전화를 할 수 있다는 것을 알 수 있었습니다.

  2. 프로세스 ID(PID)가 존재하는지 확인하는 방법

    1. 나의 대답
  3. Bash 배열에서 요소를 제거합니다. Bash 배열의 요소는 실제로 삭제되는 것이 아니라 "설정 해제"됩니다.그 의미에 대해서는, 상기의 코드의 코멘트를 참조해 주세요.

  4. 파일 사용 truebash에서 무한 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위는 실패 시 스크립트를 중지합니다.

expect1이치노

프로세스를 대기하기 전에 프로세스가 완료되는 경우가 있습니다.이미 완료된 프로세스에 대해 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 -pPID pid 집 pid 니 、아래 스크립트에 나타나 있듯이 자녀가 이미 종료된 경우에는 이 기능이 작동하지 않습니다. '불러서'라고 것이었다.wait -nN번, 여기서 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