다음과 같이 bash 스크립트가 있다고 가정하십시오.
echo "x" &
echo "y" &
echo "z" &
.....
echo "Z" &
wait
하위 쉘 / 하위 프로세스의 종료 코드를 수집하는 방법이 있습니까? 이 작업을 수행하는 방법을 찾고 있으며 아무것도 찾을 수 없습니다. 이 서브 쉘을 병렬로 실행해야합니다. 그렇지 않으면 더 쉽습니다.
일반 솔루션을 찾고 있습니다 (알 수없는 / 동적 수의 하위 프로세스가 병렬로 실행됩니다).
답변
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
답변
wait
PID와 함께 사용하면 다음 과 같은 이점 이 있습니다.
각 프로세스 ID pid 또는 작업 스펙 jobspec에 의해 지정된 하위 프로세스가 종료 될 때까지 기다렸다가 마지막으로 대기 한 명령의 종료 상태를 리턴하십시오.
각 프로세스의 PID를 저장해야합니다.
echo "x" & X=$!
echo "y" & Y=$!
echo "z" & Z=$!
스크립트에서 작업 제어를 set -m
사용하고 %n
작업 스펙을 사용할 수도 있지만 작업 제어를 원하지 않는 것은 다른 부작용이 많습니다 .
wait
프로세스가 완료된 것과 동일한 코드를 반환합니다. 당신은 사용할 수 있습니다 wait $X
로 최종 코드에 액세스 할 수있는 (합리적인) 나중에에 $?
참 / 거짓으로하거나 사용 :
echo "x" & X=$!
echo "y" & Y=$!
...
wait $X
echo "job X returned $?"
wait
명령이 아직 완료되지 않은 경우 명령이 완료 될 때까지 일시 중지됩니다.
그렇게 멈추지 않으려면 on을 설정하고trap
SIGCHLD
종료 수를 세고 wait
모든 작업이 끝나면 모든 s를 한 번에 처리 할 수 있습니다. wait
거의 항상 혼자 사용 하는 것이 좋습니다.
답변
명령을 식별하는 좋은 방법이 있다면 종료 코드를 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 파일을 제거하는 것을 잊지 마십시오.
답변
다음을 사용하십시오 compound command
-문장을 괄호 안에 넣으십시오.
( echo "x" ; echo X: $? ) &
( true ; echo TRUE: $? ) &
( false ; echo FALSE: $? ) &
출력을 줄 것이다
x
X: 0
TRUE: 0
FALSE: 1
여러 명령을 병렬로 실행하는 다른 방법은 GNU Parallel 을 사용하는 것 입니다. 실행할 명령 목록을 작성하여 파일에 넣으십시오 list
.
cat > list
sleep 2 ; exit 7
sleep 3 ; exit 55
^D
모든 명령을 병렬로 실행하고 파일에서 종료 코드를 수집하십시오 job.log
.
cat list | parallel -j0 --joblog job.log
cat job.log
출력은 다음과 같습니다.
Seq Host Starttime JobRuntime Send Receive Exitval Signal Command
1 : 1486892487.325 1.976 0 0 7 0 sleep 2 ; exit 7
2 : 1486892487.326 3.003 0 0 55 0 sleep 3 ; exit 55
답변
이것은 당신이 찾고있는 일반적인 스크립트입니다. 유일한 단점은 명령이 따옴표로 묶여 있다는 것입니다. IDE를 통한 구문 강조는 실제로 작동하지 않습니다. 그렇지 않으면 다른 답변을 시도했지만 이것이 가장 좋습니다. 이 답변에는 wait <pid>
@Michael이 제공하는 사용 아이디어가 포함되어 있지만 trap
가장 잘 작동 하는 명령을 사용하여 한 단계 더 나아갑니다 .
#!/usr/bin/env bash
set -m # allow for job control
EXIT_CODE=0; # exit code of overall script
function handleJobs() {
for job in `jobs -p`; 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
}
trap 'handleJobs' CHLD # trap command is the key part
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
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; # wait for all subshells to finish
echo "EXIT_CODE => $EXIT_CODE"
exit "$EXIT_CODE"
# end
올바른 길을 안내 해준 @michael homer에게 감사하지만, trap
명령을 사용 하는 것이 AFAICT의 가장 좋은 방법입니다.
답변
@rolf의 또 다른 변형은 다음과 같습니다.
종료 상태를 저장하는 다른 방법은 다음과 같습니다.
mkdir /tmp/status_dir
그런 다음 각 스크립트를
script_name="${0##*/}" ## strip path from script name
tmpfile="/tmp/status_dir/${script_name}.$$"
do something
rc=$?
echo "$rc" > "$tmpfile"
이를 통해 생성 한 스크립트의 이름과 프로세스 ID (동일한 스크립트의 둘 이상의 인스턴스가 실행중인 경우)를 포함하여 각 상태 파일의 고유 이름을 제공하여 나중에 참조 할 수 있도록 저장할 수 있습니다. 같은 위치에 있으므로 완료되면 전체 하위 디렉토리를 삭제할 수 있습니다.
다음과 같은 작업을 수행하여 각 스크립트에서 둘 이상의 상태를 저장할 수도 있습니다.
tmpfile="$(/bin/mktemp -q "/tmp/status_dir/${script_name}.$$.XXXXXX")"
이전과 같이 파일을 생성하지만 고유 한 임의 문자열을 추가합니다.
또는 동일한 파일에 더 많은 상태 정보를 추가 할 수 있습니다.
답변
script3
경우에만 실행됩니다 script1
및 script2
성공과 script1
및 script2
병렬로 실행됩니다 :
./script1 &
process1=$!
./script2 &
process2=$!
wait $process1
rc1=$?
wait $process2
rc2=$?
if [[ $rc1 -eq 0 ]] && [[ $rc2 -eq 0 ]];then
./script3
fi