![병렬 백그라운드 프로세스(서브셸)의 종료 코드 수집](https://linux55.com/image/105300/%EB%B3%91%EB%A0%AC%20%EB%B0%B1%EA%B7%B8%EB%9D%BC%EC%9A%B4%EB%93%9C%20%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4(%EC%84%9C%EB%B8%8C%EC%85%B8)%EC%9D%98%20%EC%A2%85%EB%A3%8C%20%EC%BD%94%EB%93%9C%20%EC%88%98%EC%A7%91.png)
다음과 같은 bash 스크립트가 있다고 가정해 보겠습니다.
echo "x" &
echo "y" &
echo "z" &
.....
echo "Z" &
wait
서브쉘/하위 프로세스의 종료 코드를 수집하는 방법이 있습니까? 이 작업을 수행할 방법을 찾고 있지만 아무것도 찾을 수 없습니다.이 서브셸을 병렬로 실행해야 합니다. 그렇지 않으면 더 쉬울 것입니다.
나는 ~을 찾고 있다일반적인해결책(병렬로 실행되는 알 수 없는/동적 개수의 하위 프로세스가 있습니다).
답변1
사용wait
PID를 사용하는 경우할 것이다:
각 프로세스 ID로 지정된 하위 프로세스를 기다립니다.PID또는 작업 사양작업 사양기다렸던 마지막 명령의 종료 상태를 종료하고 반환합니다.
항상 각 프로세스의 PID를 저장해야 합니다.
echo "x" & X=$!
echo "y" & Y=$!
echo "z" & Z=$!
스크립트에서 작업 제어를 활성화 set -m
하고 작업 사양을 사용할 수도 %n
있지만 거의 원하지 않을 것입니다.직업 통제에는 다른 많은 부작용이 있다.
wait
프로세스가 완료될 때와 동일한 코드가 반환됩니다. wait $X
as를 사용하여 미래의 (합리적인) 시점에 최종 코드에 액세스하거나 $?
간단히 true/false로 사용할 수 있습니다 .
echo "x" & X=$!
echo "y" & Y=$!
...
wait $X
echo "job X returned $?"
wait
명령이 아직 완료되지 않은 경우 명령이 완료될 때까지 일시 중지됩니다.
이런 침체를 피하고 싶다면,trap
켜짐 으로 설정SIGCHLD
, 종료 횟수를 계산하고 wait
모두 완료되는 즉시 모든 작업을 처리합니다. wait
거의 항상 혼자 사용할 수 있습니다 .
답변2
HandleJobs를 사용한 Alexander Mills의 답변은 좋은 출발점을 제공했지만 이 오류도 발생했습니다.
경고: run_pending_traps: Trap_list[17]의 잘못된 값: 0x461010
대신 각 하위 항목의 pid를 저장한 다음 기다렸다가 각 하위 항목의 종료 코드를 구체적으로 가져옵니다. 나는 함수에서 자식 프로세스를 생성하고 자식 프로세스를 기다리고 싶었던 부모 프로세스를 기다리는 위험을 피하는 더 깔끔한 방법을 찾았습니다. 트랩을 사용하지 않았기 때문에 무슨 일이 일어났는지 더 명확해졌습니다.
#!/usr/bin/env bash
# it seems it does not work well if using echo for function return value, and calling inside $() (is a subprocess spawned?)
function wait_and_get_exit_codes() {
children=("$@")
EXIT_CODE=0
for job in "${children[@]}"; do
echo "PID => ${job}"
CODE=0;
wait ${job} || CODE=$?
if [[ "${CODE}" != "0" ]]; then
echo "At least one test failed with exit code => ${CODE}" ;
EXIT_CODE=1;
fi
done
}
DIRN=$(dirname "$0");
commands=(
"{ echo 'a'; exit 1; }"
"{ echo 'b'; exit 0; }"
"{ echo 'c'; exit 2; }"
)
clen=`expr "${#commands[@]}" - 1` # get length of commands - 1
children_pids=()
for i in `seq 0 "$clen"`; do
(echo "${commands[$i]}" | bash) & # run the command via bash in subshell
children_pids+=("$!")
echo "$i ith command has been issued as a background job"
done
# wait; # wait for all subshells to finish - its still valid to wait for all jobs to finish, before processing any exit-codes if we wanted to
#EXIT_CODE=0; # exit code of overall script
wait_and_get_exit_codes "${children_pids[@]}"
echo "EXIT_CODE => $EXIT_CODE"
exit "$EXIT_CODE"
# end
답변3
다른 답변은 불필요하게 복잡하다고 생각합니다.
배경 PID를 시작할 때 배열에 저장하면 이를 명시적으로 기다리고 두 번째 배열에서 반환 코드를 수집할 수 있습니다.
pids=()
echo "x" & pids+=($!)
echo "y" & pids+=($!)
echo "z" & pids+=($!)
....
echo "Z" & pids+=($!)
# wait and collect return codes
rets=()
for pid in ${pids[*]}; do
wait $pid
rets+=($?)
done
echo "Return codes: ${rets[*]}"
반환 코드 수집같은 순서로직업의 도입과 함께.
하나 이상의 작업이 실패했는지 확인하려는 경우에는 모든 반환 코드를 수집할 필요가 없습니다.
error=false
for pid in ${pids[*]}; do
if ! wait $pid; then
error=true
fi
done
답변4
명령을 식별하는 좋은 방법이 있는 경우 종료 코드를 tmp 파일에 인쇄한 다음 관심 있는 특정 파일에 액세스할 수 있습니다.
#!/bin/bash
for i in `seq 1 5`; do
( sleep $i ; echo $? > /tmp/cmd__${i} ) &
done
wait
for i in `seq 1 5`; do # or even /tmp/cmd__*
echo "process $i:"
cat /tmp/cmd__${i}
done
tmp 파일을 삭제하는 것을 잊지 마십시오.