Manera confiable de obtener el código de salida de un proceso en segundo plano mientras lo monitorea y lo elimina cuando sea necesario

Manera confiable de obtener el código de salida de un proceso en segundo plano mientras lo monitorea y lo elimina cuando sea necesario

Se me ocurrió una configuración que imagino que hará eso, excepto que no funciona:

#!/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"

En la primera ejecución, parece que todo está bien, puedo finalizar el proceso touch kill_flag; de lo contrario, espera hasta que myprog finalice normalmente. Pero luego me di cuenta de que siempre obtengo el -1 en recuperación. myprog devuelve 0 según lo confirmado por una ejecución normal. Una investigación adicional indicó que la echo $?parte " " se ejecutó inmediatamente después de iniciar el script, no después de que finalice el comando de espera. Me pregunto qué está pasando aquí. Soy bastante nuevo en esto.

Respuesta1

waitsólo puede funcionar en hijos del proceso de shell actual. La subcapa que interpreta el código interno <(...)no puede esperar a un proceso hermano.

La espera tendría que realizarse mediante el mismo proceso de shell que inició el pid. Con zshen lugar de bash(aquí suponiendo que no se esté ejecutando ningún otro trabajo en segundo plano):

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

Respuesta2

Descubrí una versión viable:

#!/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"

Lo único molesto es que cuando mato myprog con un semáforo, aparecerá un error ya que la sustitución del proceso está muerta, pero se puede atrapar fácilmente.

información relacionada