영원히 걸리는 내 메인 bourne 쉘 스크립트에서 호출 된 명령 CMD가 있습니다.
다음과 같이 스크립트를 수정하고 싶습니다.
- CMD 명령을 백그라운드 프로세스로 병렬로 실행합니다 (
CMD &
). - 기본 스크립트에서 몇 초마다 생성 된 명령을 모니터링하는 루프가 있습니다. 루프는 또한 스크립트의 진행을 나타내는 일부 메시지를 stdout에 에코합니다.
- 생성 된 명령이 종료되면 루프를 종료합니다.
- 생성 된 프로세스의 종료 코드를 캡처하고보고합니다.
누군가 나에게 이것을 달성하기위한 지침을 줄 수 있습니까?
답변
1 : bash에서 $!
실행 된 마지막 백그라운드 프로세스의 PID를 보유합니다. 어쨌든 모니터링 할 프로세스를 알려줍니다.
4 : wait <n>
PID <n>
가 있는 프로세스 가 완료 될 때까지 기다린 다음 (프로세스 가 완료 될 때까지 차단되므로 프로세스가 완료 될 때까지이를 호출하지 않을 수 있음) 완료된 프로세스의 종료 코드를 반환합니다.
2, 3 : ps
또는 ps | grep " $! "
프로세스가 아직 실행 중인지 여부를 알 수 있습니다. 출력을 이해하고 마무리에 얼마나 가까운지를 결정하는 방법은 사용자에게 달려 있습니다. ( ps | grep
바보가 아닙니다. 시간이 있다면 프로세스가 여전히 실행 중인지 여부를 알 수있는 더 강력한 방법을 찾을 수 있습니다.)
다음은 스켈레톤 스크립트입니다.
# simulate a long process that will have an identifiable exit code
(sleep 15 ; /bin/false) &
my_pid=$!
while ps | grep " $my_pid " # might also need | grep -v grep here
do
echo $my_pid is still in the ps output. Must still be running.
sleep 3
done
echo Oh, it looks like the process is done.
wait $my_pid
# The variable $? always holds the exit code of the last command to finish.
# Here it holds the exit code of $my_pid, since wait exits with that code.
my_status=$?
echo The exit status of the process was $my_status
답변
비슷한 필요가있을 때 이것이 내가 해결 한 방법입니다.
# Some function that takes a long time to process
longprocess() {
# Sleep up to 14 seconds
sleep $((RANDOM % 15))
# Randomly exit with 0 or 1
exit $((RANDOM % 2))
}
pids=""
# Run five concurrent processes
for i in {1..5}; do
( longprocess ) &
# store PID of process
pids+=" $!"
done
# Wait for all processes to finish, will take max 14s
# as it waits in order of launch, not order of finishing
for p in $pids; do
if wait $p; then
echo "Process $p success"
else
echo "Process $p fail"
fi
done
답변
백그라운드 하위 프로세스의 pid는 $! . 모든 자식 프로세스의 pid를 배열에 저장할 수 있습니다 (예 : PIDS []) .
wait [-n] [jobspec or pid …]
각 프로세스 ID pid 또는 작업 사양 jobspec에 지정된 하위 프로세스가 종료 될 때까지 기다렸다가 마지막으로 대기 한 명령의 종료 상태를 반환합니다. 작업 사양이 제공되면 작업의 모든 프로세스가 대기합니다. 인수가 제공되지 않으면 현재 활성화 된 모든 자식 프로세스가 대기하고 반환 상태는 0입니다. -n 옵션이 제공되면 작업이 종료 될 때까지 기다렸다가 종료 상태를 반환합니다. jobspec도 pid도 쉘의 활성 자식 프로세스를 지정하지 않으면 반환 상태는 127입니다.
wait 명령을 사용 하면 모든 하위 프로세스가 완료 될 때까지 기다릴 수 있으며 $? 를 통해 각 하위 프로세스의 종료 상태를 얻을 수 있습니다 . 상태를 STATUS []에 저장합니다 . 그러면 상태에 따라 무언가를 할 수 있습니다.
다음 두 가지 솔루션을 시도했으며 잘 실행됩니다. solution01 은 더 간결하지만 solution02 는 약간 복잡합니다.
solution01
#!/bin/bash
# start 3 child processes concurrently, and store each pid into array PIDS[].
process=(a.sh b.sh c.sh)
for app in ${process[@]}; do
./${app} &
PIDS+=($!)
done
# wait for all processes to finish, and store each process's exit code into array STATUS[].
for pid in ${PIDS[@]}; do
echo "pid=${pid}"
wait ${pid}
STATUS+=($?)
done
# after all processed finish, check their exit codes in STATUS[].
i=0
for st in ${STATUS[@]}; do
if [[ ${st} -ne 0 ]]; then
echo "$i failed"
else
echo "$i finish"
fi
((i+=1))
done
solution02
#!/bin/bash
# start 3 child processes concurrently, and store each pid into array PIDS[].
i=0
process=(a.sh b.sh c.sh)
for app in ${process[@]}; do
./${app} &
pid=$!
PIDS[$i]=${pid}
((i+=1))
done
# wait for all processes to finish, and store each process's exit code into array STATUS[].
i=0
for pid in ${PIDS[@]}; do
echo "pid=${pid}"
wait ${pid}
STATUS[$i]=$?
((i+=1))
done
# after all processed finish, check their exit codes in STATUS[].
i=0
for st in ${STATUS[@]}; do
if [[ ${st} -ne 0 ]]; then
echo "$i failed"
else
echo "$i finish"
fi
((i+=1))
done
답변
거의 모든 답변 ps
이 백그라운드 프로세스의 상태를 폴링하기 위해 외부 유틸리티 (대부분 )를 사용 합니다. SIGCHLD 신호를 포착하는 더 많은 unixesh 솔루션이 있습니다. 신호 처리기에서 어떤 자식 프로세스가 중지되었는지 확인해야합니다. 그것은하여 수행 할 수 있습니다 kill -0 <PID>
(보편적 인) 내장 또는 존재 확인 /proc/<PID>
디렉토리 (리눅스 특정을) 또는 사용하여 jobs
내장 (세게 때리다특유한. jobs -l
또한 pid를보고합니다. 이 경우 출력의 세 번째 필드는 Stopped | Running | Done | Exit 일 수 있습니다. ).
여기 제 예가 있습니다.
시작된 프로세스를라고 loop.sh
합니다. -x
인수로 또는 숫자를 받습니다 . For -x
는 종료 코드 1로 종료됩니다. 숫자에 대해 num * 5 초를 기다립니다. 5 초마다 PID를 인쇄합니다.
런처 프로세스는 다음과 같이 호출됩니다 launch.sh
.
#!/bin/bash
handle_chld() {
local tmp=()
for((i=0;i<${#pids[@]};++i)); do
if [ ! -d /proc/${pids[i]} ]; then
wait ${pids[i]}
echo "Stopped ${pids[i]}; exit code: $?"
else tmp+=(${pids[i]})
fi
done
pids=(${tmp[@]})
}
set -o monitor
trap "handle_chld" CHLD
# Start background processes
./loop.sh 3 &
pids+=($!)
./loop.sh 2 &
pids+=($!)
./loop.sh -x &
pids+=($!)
# Wait until all background processes are stopped
while [ ${#pids[@]} -gt 0 ]; do echo "WAITING FOR: ${pids[@]}"; sleep 2; done
echo STOPPED
자세한 설명은 bash 스크립트에서 프로세스 시작 실패를 참조하십시오.
답변
#/bin/bash
#pgm to monitor
tail -f /var/log/messages >> /tmp/log&
# background cmd pid
pid=$!
# loop to monitor running background cmd
while :
do
ps ax | grep $pid | grep -v grep
ret=$?
if test "$ret" != "0"
then
echo "Monitored pid ended"
break
fi
sleep 5
done
wait $pid
echo $?
답변
나는 당신의 접근 방식을 약간 바꿀 것입니다. 명령이 여전히 활성 상태인지 몇 초마다 확인하고 메시지를보고하는 대신 명령이 아직 실행 중임을 몇 초마다보고하는 다른 프로세스를 사용하고 명령이 완료되면 해당 프로세스를 종료하십시오. 예를 들면 :
#! / bin / sh cmd () {수면 5; 24 번 출구; } cmd & # 장기 실행 프로세스 실행 pid = $! # PID 기록 # 명령이 여전히 실행 중임을 계속보고하는 프로세스를 생성합니다. while echo "$ (date) : $ pid is still running"; 잠을 1; 완료 & echoer = $! # 프로세스가 완료되면 리포터를 죽이는 트랩 설정 트랩 '$ echoer'0 # 프로세스가 끝날 때까지 기다립니다 기다리면 $ pid; 그때 echo "cmd 성공" 그밖에 echo "cmd FAILED !! (반환 된 $?)" fi
답변
우리 팀은 25 분 동안 활동이 없으면 시간이 초과되는 원격 SSH 실행 스크립트에 대해 동일한 요구를 받았습니다. 다음은 모니터링 루프가 매초 백그라운드 프로세스를 확인하지만 비활성 시간 초과를 억제하기 위해 10 분마다 인쇄하는 솔루션입니다.
long_running.sh &
pid=$!
# Wait on a background job completion. Query status every 10 minutes.
declare -i elapsed=0
# `ps -p ${pid}` works on macOS and CentOS. On both OSes `ps ${pid}` works as well.
while ps -p ${pid} >/dev/null; do
sleep 1
if ((++elapsed % 600 == 0)); then
echo "Waiting for the completion of the main script. $((elapsed / 60))m and counting ..."
fi
done
# Return the exit code of the terminated background process. This works in Bash 4.4 despite what Bash docs say:
# "If neither jobspec nor pid specifies an active child process of the shell, the return status is 127."
wait ${pid}