código de retorno confiável do processo em segundo plano

10

Vamos supor a seguinte parte do código bash:

foo > logfile 2>&1 &
foo_pid=$!

while ps -p$foo_pid
do
    ping -c 1 localhost
done

wait $foo_pid

if [[ $? == 0 ]]
then
    echo "foo success"
fi

É seguro assumir que $? realmente contém o código de retorno de foo e não o código de retorno de ping ? Se a resposta a essa pergunta for: "Você não pode presumir isso". então como posso modificar esse trecho de código para ter certeza de que $? sempre contém o código de retorno de foo ?

    
por Christopher Schmidt 20.02.2014 / 14:43

3 respostas

11

Com bash , você terá essa garantia a menos que tenha iniciado outra tarefa em segundo plano (e esteja ciente de que as tarefas em segundo plano podem ser iniciadas com & , mas também com coproc e com substituição de processo) entre foo & e wait .

O POSIX exige que um shell se lembre do status de saída de pelo menos 25 jobs depois que eles saírem , mas bash lembra muito mais que isso.

Agora, se você fizer isso:

foo & pid=$!
...
bar &
wait "$pid"

Você não tem garantia de que bar não receberá o mesmo pid de foo (se foo tiver terminado no momento em que bar iniciar), portanto, mesmo que seja improvável, wait "$pid" pode dar-lhe o status de saída de bar .

Você pode reproduzi-lo com:

bash -c '(exit 12; foo) & pid=$!
         while : bar & [ "$pid" != "$!" ]; do :;done
         wait "$pid"; echo "$?"'

que (eventualmente) lhe dará 0 em vez de 12 .

Para evitar o problema, uma maneira seria escrevê-lo como:

{
  foo_pid=$!

  while ps -p "$foo_pid"
  do
      ping -c 1 localhost
  done

  bar &
  ...

  read <&3 ret
  if [ "$ret" = 0 ]; then
    echo foo was sucessful.
  fi
} 3< <(foo > logfile 2>&1; echo "$?")
    
por 20.02.2014 / 15:07
4

Sim, você pode confiar em wait "$!" para obter o status de um trabalho em segundo plano. Quando executado como um script, o bash não coleta automaticamente os trabalhos em segundo plano concluídos. Portanto, se você executar wait , ele coletará o trabalho no momento em que wait for chamado.

Você pode testar isso com um script simples:

#!/bin/bash
sh -c 'sleep 1; exit 22' &
sleep 5
echo "FG: $?"
wait %1
echo "BG: $?"

Qual será a saída:

FG: 0
BG: 22
    
por 20.02.2014 / 15:01
0

Eu acredito que sua suposição está correta. Aqui está uma extração de man bash sobre a espera por processos em segundo plano.

If n specifies a non-existent process or job, the return status is 127. Otherwise, the return status is the exit status of the last process or job waited for.

Então talvez você deva checar por 127

Há uma pergunta semelhante com uma resposta completamente diferente do que poderia ajudar.

Script Bash aguarda os processos e obtém o código de retorno

editar 1

Inspirado nos comentários e respostas do @Stephane, eu expandi seu roteiro. Eu posso começar cerca de 34 processos em segundo plano antes de começar a perder o controle.

tback

$ cat tback 
plist=()
elist=()
slist=([1]=12 [2]=15 [3]=17 [4]=19 [5]=21 [6]=23)
count=30

#start background tasksto monitor
for i in 1 2 3 4
do
  #echo pid $i ${plist[$i]} ${slist[$i]}
  (echo $BASHPID-${slist[$i]} running; exit ${slist[$i]}) & 
  plist[$i]=$!
done

echo starting $count background echos to test history
for i in 'eval echo {1..$count}'
do
  echo -n "." &
  elist[$i]=$! 
done
# wait for each background echo to complete
for i in 'eval echo {1..$count}'
do
  wait ${elist[$i]}
  echo -n $? 
done
echo ""
# Now wait for each monitored process and check return status with expected
failed=0
for i in 1 2 3 4
do
  wait ${plist[$i]}
  rv=$?
  echo " pid ${plist[$i]} returns $rv should be ${slist[$i]}"
  if [[ $rv != ${slist[$i]} ]] 
  then
    failed=1
  fi
done

wait
echo "Complete $failed"
if [[ $failed = "1" ]]
then
  echo Failed
else
  echo Success
fi
exit $failed
$ 

no meu sistema produz

$ bash tback
14553-12 running
14554-15 running
14555-17 running
starting 30 background echos to test history
14556-19 running
..............................000000000000000000000000000000
 pid 14553 returns 12 should be 12
 pid 14554 returns 15 should be 15
 pid 14555 returns 17 should be 17
 pid 14556 returns 19 should be 19
Complete 0
Success
    
por 20.02.2014 / 15:01