รหัสส่งคืนที่เชื่อถือได้ของกระบวนการพื้นหลัง


14

สมมติว่าชิ้นส่วนของรหัสทุบตีต่อไปนี้:

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

มีความปลอดภัยหรือไม่ที่จะสมมติว่า$?มีรหัสส่งคืนของfooและไม่ใช่รหัสส่งคืนpingใช่หรือไม่ หากคำตอบของคำถามนั้นคือ: "คุณไม่สามารถสันนิษฐานได้" แล้วฉันจะแก้ไขรหัสชิ้นนี้เพื่อให้แน่ใจว่า$?มีรหัสที่ส่งคืนได้fooอย่างไร

คำตอบ:


12

ด้วยbashคุณจะมีการรับประกันว่าถ้าคุณได้เริ่มต้นงานพื้นหลังอื่น (และระวังว่างานพื้นหลังสามารถเริ่มต้นด้วย&แต่ยังมีcoprocและมีขั้นตอนการเปลี่ยนตัว) ระหว่างและfoo &wait

POSIX ต้องการให้เชลล์จดจำสถานะการออกอย่างน้อย 25 งานหลังจากที่พวกเขาหายไปแต่bashจำได้มากกว่านั้น

ตอนนี้ถ้าคุณทำ:

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

คุณได้มีการรับประกันว่าbarจะไม่ได้รับ pid เดียวกับfoo(ถ้าfooมีการยกเลิกตามเวลาที่barเริ่มต้น) ดังนั้นแม้ว่ามันจะไม่น่าอาจทำให้คุณออกจากสถานะของwait "$pid"bar

คุณสามารถทำซ้ำได้ด้วย:

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

ซึ่งจะให้ในที่สุด ( 0แทน12)

เพื่อหลีกเลี่ยงปัญหาวิธีหนึ่งคือการเขียนเป็น:

{
  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

ใช่คุณสามารถพึ่งพาwait "$!"เพื่อรับสถานะของงานพื้นหลัง เมื่อรันเป็นสคริปต์ทุบตีจะไม่รวบรวมงานพื้นหลังที่เสร็จสมบูรณ์โดยอัตโนมัติ ดังนั้นหากคุณเรียกใช้waitมันจะรวบรวมงานในเวลาที่waitเรียกว่า

คุณสามารถทดสอบสิ่งนี้ด้วยสคริปต์ง่ายๆ:

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

ซึ่งจะส่งออก:

FG: 0
BG: 22

ส่วนสำคัญของคำสั่งนั้นคือจุดเริ่มต้น "เมื่อเรียกใช้เป็นสคริปต์" เมื่อมีการโต้ตอบwaitไม่ทำงาน กระบวนการจะถูกรวบรวมและสถานะการออกจะถูกทิ้งก่อนที่จะปรากฏข้อความแจ้งเตือน (ตามค่าเริ่มต้น)
แพทริค

ฉันเพิ่งลองใช้ bash 4.2.37, 4.1.2 และ 3.2.48 พวกเขาทั้งหมดทำตัวเหมือนกัน (คัดลอก / วางรหัสตามคำตอบของฉัน) wait %1ล้มเหลวด้วย "ไม่มีงานดังกล่าว" เป็นกระบวนการพื้นหลังเป็นที่เก็บรวบรวมทันทีหลังจากที่ "นอน 5" เสร็จสมบูรณ์
Patrick

โอเคขอโทษฉันเข้าใจแล้ว ฉันได้พลาดของคุณในสถานที่ของ%1 $!
Stéphane Chazelas

โปรดทราบว่าbash -c '(sleep 1;exit 5) & sleep 2; wait %1; echo $?'(เช่นไม่โต้ตอบเช่นกัน) ล้มเหลวในการรับสถานะการออกของงานที่ตายแล้ว ฟังดูเหมือนแมลง
Stéphane Chazelas

นี้ไม่ได้ทำงานให้ฉันภายในสูตร Makefile set +eจนกว่าฉันจะรวม ดูเหมือนว่าset -eฟังก์ชั่นของ bash จะฆ่าสคริปต์ทันทีที่รหัสทางออกไม่ดีถูกยกขึ้นโดยwait
user5359531

0

ฉันเชื่อว่าสมมติฐานของคุณถูกต้อง นี่คือสารสกัดจากการman bashรอกระบวนการพื้นหลัง

หาก n ระบุกระบวนการหรืองานที่ไม่มีอยู่สถานะการส่งคืนคือ 127 มิฉะนั้นสถานะการส่งคืนคือสถานะการออกของกระบวนการล่าสุดหรืองานที่รอ

ดังนั้นบางทีคุณควรตรวจสอบ 127

มีคำถามที่คล้ายกันกับคำตอบที่แตกต่างไปจากเดิมอย่างสิ้นเชิงอาจช่วยได้

สคริปต์ Bash รอกระบวนการและรับรหัสส่งคืน

แก้ไข 1

ได้รับแรงบันดาลใจจากความคิดเห็นและคำตอบของ @ Stephane ฉันได้ขยายสคริปต์ของเขา ฉันสามารถเริ่มกระบวนการพื้นหลังได้ประมาณ 34 ครั้งก่อนที่มันจะเริ่มหลวม

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
$ 

ในระบบของฉันผลิต

$ 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
ไม่เห็นความคิดเห็นของฉันที่จะตอบ umlauteและลองด้วยตัวคุณเองด้วยbash -c '(exit 12) & sleep 1; wait "$!"; echo "$?"'
Stéphane Chazelas

ฉันไม่เคยเห็นbashแทร็คที่หลวม (แม้หลังจากเริ่มงานนับพัน) ตัวอย่างของฉันแสดงให้เห็นถึงการใช้หมายเลข pid ซึ่งอาจเป็นสิ่งที่คุณสังเกตเห็นในกรณีของคุณเช่นกัน
Stéphane Chazelas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.