[linux] 백그라운드 프로세스의 종료 코드 가져 오기

영원히 걸리는 내 메인 bourne 쉘 스크립트에서 호출 된 명령 CMD가 있습니다.

다음과 같이 스크립트를 수정하고 싶습니다.

  1. CMD 명령을 백그라운드 프로세스로 병렬로 실행합니다 ( CMD &).
  2. 기본 스크립트에서 몇 초마다 생성 된 명령을 모니터링하는 루프가 있습니다. 루프는 또한 스크립트의 진행을 나타내는 일부 메시지를 stdout에 에코합니다.
  3. 생성 된 명령이 종료되면 루프를 종료합니다.
  4. 생성 된 프로세스의 종료 코드를 캡처하고보고합니다.

누군가 나에게 이것을 달성하기위한 지침을 줄 수 있습니까?



답변

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}