Maneira confiável de obter o código de saída de um processo em segundo plano enquanto monitora e interrompe quando necessário

Maneira confiável de obter o código de saída de um processo em segundo plano enquanto monitora e interrompe quando necessário

Eu criei uma configuração que imagino que fará isso, exceto que não 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"

Na primeira execução parece tudo bem, posso encerrar o processo touch kill_flag, caso contrário ele espera até que o myprog termine normalmente. Mas então percebi que sempre recebo -1 em retval. myprog retorna 0 conforme confirmado por uma execução normal. Investigações adicionais indicaram que a echo $?parte " " foi executada imediatamente após o lançamento do script, e não após a saída do comando wait. Estou me perguntando o que está acontecendo aqui. Eu sou muito novo no bash.

Responder1

waitsó pode funcionar em filhos do processo shell atual. O subshell que interpreta o código interno <(...)não pode esperar por um processo irmão.

A espera teria que ser feita pelo mesmo processo shell que iniciou o pid. Com zshem vez de bash(aqui, assumindo que não há outro trabalho em segundo plano em execução):

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

Responder2

Descobri uma versão viável:

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

A única coisa irritante é que quando eu mato myprog com um semáforo, um erro surgirá porque a substituição do processo está morta, mas pode ser facilmente capturada.

informação relacionada