arka plan işleminin güvenilir dönüş kodu


14

Aşağıdaki bash kodu parçasını varsayalım:

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

$?Gerçekten de dönüş kodunun foodeğil, dönüş kodunu içerdiğini varsaymak güvenli pingmidir? Bu sorunun cevabı: "Bunu kabul edemezsiniz." o $?zaman her zaman dönüş kodunu içerdiğinden emin olmak için bu kod parçasını nasıl değiştirebilirim foo?

Yanıtlar:


12

İle bash, başka bir arka plan işi başladıktan sürece bu garantiyi gerekecek (ve dikkat bu arka plan işleri ile başlatılabilir &ama aynı zamanda birlikte coprocaralarında ve süreç ikamesi ile) foo &ve wait.

POSIX, bir kabuğun gittikten sonra en az 25 işin çıkış durumunubash hatırlamasını , ancak bundan çok daha fazlasını hatırlamasını gerektirir.

Şimdi, eğer yaparsanız:

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

( Zaman başladıktan sonra sona barerdiyse) ile aynı pid'in verilmeyeceğine dair bir garantiniz yok , bu yüzden olası olmasa bile size çıkış durumunu verebilir .foofoobarwait "$pid"bar

Şununla çoğaltabilirsiniz:

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

ki (sonunda) size 0bunun yerine verecek 12.

Problemden kaçınmak için bir yol şöyle yazar:

{
  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 "$?")

4

Evet, wait "$!"bir arka plan işinin durumunu almak için güvenebilirsiniz . Komut dosyası olarak çalışırken, bash tamamlanan arka plan işlerini otomatik olarak toplamaz. Böylece koşarsanız wait, o zaman iş arayacak waitdenir.

Bunu basit bir komut dosyasıyla test edebilirsiniz:

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

Hangi çıktı:

FG: 0
BG: 22

Bu ifadenin anahtar kısmı, "komut dosyası olarak çalıştırılırken" başlangıcıydı. Etkileşimli olduğunda waitçalışmaz. İşlem toplanır ve komut istemi görüntülenmeden hemen önce çıkış durumu atılır (varsayılan olarak).
Patrick

Ben sadece 4.2.37, 4.1.2 ve 3.2.48 bash üzerinde denedim. Hepsi tam olarak aynı davranıyor (cevabımdaki kodun gerçek kopyası / yapıştırması). wait %1Arka plan işlemi "uyku 5" tamamlanıncaya hemen sonra toplanır olarak "Böyle bir işi" ile başarısız olur.
Patrick

Ah tamam, üzgünüm, şimdi anladım. Ben senin kaçırdığı %1arasında yer $!.
Stéphane Chazelas

O Not bash -c '(sleep 1;exit 5) & sleep 2; wait %1; echo $?'(iyi şekilde etkileşimli olmayan) ölü işin çıkış durumunu almak için başarısız olur. Kulağa bir böcek gibi geliyor.
Stéphane Chazelas

Ben dahil olmadan bu bir Makefile tarifi içinde benim için işe yaramadı set +e. set -eKötü bir çıkış kodu ortaya çıkar çıkmaz bash işlevinin komut dosyasını öldürdüğü wait
anlaşılıyor

0

Varsayımınızın doğru olduğuna inanıyorum. İşte man basharka plan süreçlerini beklemeyle ilgili bir alıntı .

N, var olmayan bir işlemi veya işi belirtirse, dönüş durumu 127 olur. Aksi takdirde, dönüş durumu, beklenen son işlemin veya işin çıkış durumudur.

Yani belki 127'yi kontrol etmelisin

Cevabından tamamen farklı bir cevabı olan benzer bir soru var.

Bash script süreçleri bekle ve dönüş kodunu al

düzenleme 1

@ Stephane'nin yorumlarından ve cevaplarından esinlenerek senaryosunu genişlettim. Parçayı kaybetmeye başlamadan önce yaklaşık 34 arkaplan sürecine başlayabilirim.

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
$ 

benim sistemimde

$ 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

1
Hayır, umlaute'un cevabı hakkındaki yorumuma bakın ve kendiniz deneyinbash -c '(exit 12) & sleep 1; wait "$!"; echo "$?"'
Stéphane Chazelas

Hiç bashgevşek iz görmedim (binlerce işe başladıktan sonra bile), örneğim pid'in yeniden kullanıldığını gösteriyordu, ki bu da sizin durumunuzda gözlemlediğiniz şey olabilir.
Stéphane Chazelas
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.