Zuverlässige Möglichkeit, Exit-Code von einem Hintergrundprozess abzurufen, während dieser überwacht und bei Bedarf beendet wird

Zuverlässige Möglichkeit, Exit-Code von einem Hintergrundprozess abzurufen, während dieser überwacht und bei Bedarf beendet wird

Ich habe eine Einstellung gefunden, die das meiner Meinung nach bewerkstelligen wird, allerdings funktioniert sie nicht:

#!/bin/bash

echo "Launching a background process that may take hours to finish.."
myprog &
pid=$!
retval=
##At this time pid should hold the process id of myprog
echo "pid=${pid}"

{
    ##check if the process is still running
    psl=$(ps -f -p ${pid} | grep -E "\bmyprog\b")
    killit=
    while [[ ! -z ${psl} ]]
    do
        ##if a file named "kill_flag" is detected, kill the process
        if [[ -e "kill_flag" ]]
        then
            killit=YES
            break
        fi
        #check every 3 seconds
        sleep 3
        psl=$(ps -f -p ${pid} | grep -E "\bmyprog\b")
    done

    ##killit not set, normal exit, read from fd5
    if [[ -z ${killit} ]]
    then
        read <&5 retval
  else
    ##kill here, the wait will return and the sub process ends
    kill ${pid}
  fi

} 5< <( wait ${pid} > /dev/null 2>&1; echo $? )

echo "retval=$retval"

Beim ersten Durchlauf scheint alles in Ordnung zu sein, ich kann den Prozess mit beenden touch kill_flag, andernfalls wartet er, bis myprog normal fertig ist. Aber dann ist mir aufgefallen, dass ich bei retval immer -1 bekomme. myprog gibt 0 zurück, was bei einem normalen Durchlauf bestätigt wird. Weitere Untersuchungen haben ergeben, dass der echo $?Teil „ “ unmittelbar nach dem Start des Skripts ausgeführt wurde, nicht nach Beendigung des Wartebefehls. Ich frage mich, was hier los ist. Ich bin ziemlich neu bei Bash.

Antwort1

waitkann nur an untergeordneten Prozessen des aktuellen Shell-Prozesses arbeiten. Die Subshell, die den Code darin interpretiert, <(...)kann nicht auf einen Schwesterprozess warten.

Die Wartezeit müsste vom selben Shell-Prozess übernommen werden, der die PID gestartet hat. Anstelle zshvon bash(hier wird vorausgesetzt, dass kein anderer Hintergrundjob läuft):

cmd & pid=$!
while (($#jobstates)) {
  [[ -e killfile ]] && kill $pid
  sleep 3
}
wait $pid; echo $?

Antwort2

Habe eine praktikable Version gefunden:

#!/bin/bash
export retval=
##At this time pid should hold the process id of myprog
{
    ##This is the subshell that launched and monitoring myprog
    subsh=$!

    ##Since myprog is probably the only child process of this subsh, it should be pretty safe
    pid=$(ps -f --ppid ${subsh} | grep -E "\bmyprog\b" | gawk '{print $2}' )
    ##check if the process is still running
    psl=$(ps -f -p ${pid} | grep -E "\bmyprog\b")
    killit=
    while [[ ! -z ${psl} ]]
    do
        ##if a file named "kill_flag" is detected, kill the process
        if [[ -e "kill_flag" ]]
        then
            killit=YES
            break
        fi
        #check every 3 seconds
        sleep 3
        psl=$(ps -f -p ${pid} | grep -E "\bmyprog\b")
    done

    ##killit not set, normal exit, read from fd5
    if [[ -z ${killit} ]]
    then
        read <&5 retval
  else
    ##kill here, the wait will return and the sub process ends
    kill ${pid}
  fi

} 5< <( myprog >>logfile 2>&1; echo $? )

echo "retval=$retval"

Das einzig Ärgerliche ist, dass, wenn ich myprog mit einem Semaphor beende, ein Fehler auftritt, da die Prozessersetzung tot ist, aber dieser kann leicht abgefangen werden.

verwandte Informationen