ให้สองคำสั่งพื้นหลังยุติหนึ่งที่เหลือเมื่อออกอย่างใดอย่างหนึ่ง


14

ฉันมีสคริปต์ทุบตีง่ายๆที่เริ่มต้นเซิร์ฟเวอร์สองเครื่อง:

#!/bin/bash
(cd ./frontend && gulp serve) & (cd ./backend && gulp serve --verbose)

หากคำสั่งที่สองออกจากดูเหมือนว่าคำสั่งแรกยังคงทำงานอยู่

ฉันจะเปลี่ยนสิ่งนี้ได้อย่างไรหากคำสั่งใดคำสั่งหนึ่งจบลงคำสั่งอื่นจะถูกยกเลิก?

โปรดทราบว่าเราไม่จำเป็นต้องตรวจสอบระดับข้อผิดพลาดของกระบวนการพื้นหลังไม่ว่าจะมีการออกจาก


ทำไมไม่gulp ./fronend/serve && gulp ./backend/serve --verbose?
heemayl

serveเป็นอาร์กิวเมนต์ไม่ใช่ไฟล์ดังนั้นจึงต้องตั้งค่าไดเรกทอรีปัจจุบัน
blah238

1
นอกจากนี้ยังเป็นกระบวนการที่ใช้เวลานานซึ่งจำเป็นต้องรันพร้อมกันขออภัยหากไม่ชัดเจน
blah238

คำตอบ:


22

สิ่งนี้เริ่มต้นทั้งสองกระบวนการรอกระบวนการแรกที่เสร็จสิ้นจากนั้นจึงฆ่ากระบวนการอื่น:

#!/bin/bash
{ cd ./frontend && gulp serve; } &
{ cd ./backend && gulp serve --verbose; } &
wait -n
pkill -P $$

มันทำงานอย่างไร

  1. ราคาเริ่มต้น:

    { cd ./frontend && gulp serve; } &
    { cd ./backend && gulp serve --verbose; } &
    

    คำสั่งสองคำสั่งข้างต้นเริ่มกระบวนการทั้งสองในพื้นหลัง

  2. รอ

    wait -n

    สิ่งนี้จะรอให้งานพื้นหลังสิ้นสุดลง

    เนื่องจาก-nตัวเลือกนี้ต้องใช้ทุบตี 4.3 หรือดีกว่า

  3. ฆ่า

    pkill -P $$

    นี่จะฆ่างานใด ๆ ที่กระบวนการปัจจุบันเป็นพาเรนต์ กล่าวอีกนัยหนึ่งสิ่งนี้ฆ่ากระบวนการพื้นหลังใด ๆ ที่ยังคงทำงานอยู่

    หากระบบของคุณไม่มีpkillลองเปลี่ยนสายนี้ด้วย:

    kill 0

    ซึ่งฆ่ากลุ่มกระบวนการปัจจุบันด้วย

ตัวอย่างที่ทดสอบได้ง่าย

โดยการเปลี่ยนสคริปต์เราสามารถทดสอบได้โดยไม่ต้องgulpติดตั้ง:

$ cat script.sh 
#!/bin/bash
{ sleep $1; echo one;  } &
{ sleep $2; echo two;  } &
wait -n
pkill -P $$
echo done

สคริปต์ข้างต้นสามารถเรียกใช้bash script.sh 1 3และกระบวนการแรกสิ้นสุดลงก่อน อีกวิธีหนึ่งสามารถเรียกใช้เป็นbash script.sh 3 1และกระบวนการที่สองจะสิ้นสุดลงก่อน ในทั้งสองกรณีเราจะเห็นว่าสิ่งนี้ทำงานได้ตามต้องการ


มันดูดีมาก น่าเสียดายที่คำสั่งเหล่านั้นใช้ไม่ได้กับสภาพแวดล้อมการทุบตีของฉัน (msysgit) นั่นเป็นสิ่งที่ไม่ดีสำหรับฉันที่ไม่ได้ระบุว่า ฉันจะลองในกล่อง Linux จริงแม้ว่า
blah238

(1) ไม่bashรองรับ-nตัวเลือกทั้งหมดในwaitคำสั่ง (2) ฉันเห็นด้วย 100% กับประโยคแรก - โซลูชันของคุณเริ่มต้นกระบวนการสองกระบวนการรอให้กระบวนการแรกเสร็จสิ้นจากนั้นจึงฆ่าอีกกระบวนการ แต่คำถามบอกว่า“ …หากคำสั่งผิดพลาดอย่างใดอย่างหนึ่งออกไปอีกจะถูกยกเลิกหรือไม่” ฉันเชื่อว่าทางออกของคุณไม่ใช่สิ่งที่ OP ต้องการ (3) ทำไมคุณ(…) &ถึงเปลี่ยนไป{ …; } &? &กองกำลังรายการ (คำสั่งกลุ่ม) ที่จะทำงานใน subshell เลยล่ะค่ะ IMHO คุณได้เพิ่มตัวละครและอาจทำให้เกิดความสับสน (ฉันต้องดูสองครั้งเพื่อทำความเข้าใจ) โดยไม่มีประโยชน์
G-Man กล่าวว่า 'Reinstate Monica'

1
จอห์นนั้นถูกต้องพวกเขาเป็นเว็บเซิร์ฟเวอร์ที่ปกติแล้วทั้งคู่ควรจะทำงานต่อไปจนกว่าจะมีข้อผิดพลาดเกิดขึ้นหรือพวกเขาถูกส่งสัญญาณเพื่อยุติ ดังนั้นฉันไม่คิดว่าเราจำเป็นต้องตรวจสอบระดับข้อผิดพลาดของแต่ละกระบวนการไม่ว่าจะยังคงทำงานอยู่หรือไม่
blah238

2
pkillไม่สามารถใช้ได้สำหรับฉัน แต่kill 0ดูเหมือนว่าจะมีผลเหมือนกัน นอกจากนี้ฉันได้อัปเดตสภาพแวดล้อม Git for Windows ของฉันและดูเหมือนว่าจะใช้wait -nงานได้ในขณะนี้ดังนั้นฉันจึงยอมรับคำตอบนี้
blah238

1
น่าสนใจ ถึงแม้ว่าผมจะเห็นว่าพฤติกรรมที่เป็นเอกสารสำหรับบางรุ่นkillของ เอกสารในระบบของฉันไม่ได้กล่าวถึง อย่างไรก็ตามkill 0ทำงานอย่างไร หาดี!
John1024

1

นี่เป็นเรื่องยุ่งยาก นี่คือสิ่งที่ฉันคิด อาจเป็นไปได้ที่จะทำให้ / ปรับปรุงง่ายขึ้น:

#!/bin/sh

pid1file=$(mktemp)
pid2file=$(mktemp)
stat1file=$(mktemp)
stat2file=$(mktemp)

while true; do sleep 42; done &
main_sleeper=$!

(cd frontend && gulp serve           & echo "$!" > "$pid1file";
    wait "$!" 2> /dev/null; echo "$?" > "$stat1file"; kill "$main_sleeper" 2> /dev/null) &
(cd backend  && gulp serve --verbose & echo "$!" > "$pid2file";
    wait "$!" 2> /dev/null; echo "$?" > "$stat2file"; kill "$main_sleeper" 2> /dev/null) &
sleep 1
wait "$main_sleeper" 2> /dev/null

if stat1=$(<"$stat1file")  &&  [ "$stat1" != "" ]  &&  [ "$stat1" != 0 ]
then
        echo "First process failed ..."
        if pid2=$(<"$pid2file")  &&  [ "$pid2" != "" ]
        then
                echo "... killing second process."
                kill "$pid2" 2> /dev/null
        fi
fi
if [ "$stat1" = "" ]  &&  \
   stat2=$(<"$stat2file")  &&  [ "$stat2" != "" ]  &&  [ "$stat2" != 0 ]
then
        echo "Second process failed ..."
        if pid1=$(<"$pid1file")  &&  [ "$pid1" != "" ]
        then
                echo "... killing first process."
                kill "$pid1" 2> /dev/null
        fi
fi

wait
if stat1=$(<"$stat1file")
then
        echo "Process 1 terminated with status $stat1."
else
        echo "Problem getting status of process 1."
fi
if stat2=$(<"$stat2file")
then
        echo "Process 2 terminated with status $stat2."
else
        echo "Problem getting status of process 2."
fi
  • ก่อนอื่นให้เริ่มกระบวนการ ( while true; do sleep 42; done &) ที่หลับ / หยุดชั่วคราวตลอดไป หากคุณแน่ใจว่าคำสั่งทั้งสองของคุณจะยุติลงภายในระยะเวลาหนึ่ง (เช่นหนึ่งชั่วโมง) คุณสามารถเปลี่ยนเป็นโหมดสลีปเดียวที่จะเกินนั้น (เช่นsleep 3600) จากนั้นคุณสามารถเปลี่ยนตรรกะต่อไปนี้เพื่อใช้สิ่งนี้เป็นการหมดเวลา นั่นคือฆ่ากระบวนการหากพวกเขายังคงทำงานหลังจากเวลามาก (โปรดทราบว่าสคริปต์ด้านบนในขณะนี้ไม่ได้ทำเช่นนั้น)
  • เริ่มต้นกระบวนการแบบอะซิงโครนัส (พื้นหลังพร้อมกัน) สองกระบวนการ
    • คุณไม่จำเป็นต้องสำหรับ./cd
    • command & echo "$!" > somewhere; wait "$!" เป็นโครงสร้างที่ซับซ้อนซึ่งเริ่มต้นกระบวนการแบบอะซิงโครนัสจับ PID ของมันแล้วรอมัน ทำให้มันเรียงลำดับของกระบวนการเบื้องหน้า (ซิงโครนัส) แต่สิ่งนี้เกิดขึ้นภายใน(…)รายการที่อยู่ในพื้นหลังอย่างครบถ้วนดังนั้นgulpกระบวนการทำงานแบบอะซิงโครนัส
    • หลังจากgulpกระบวนการอย่างใดอย่างหนึ่งจบลงให้เขียนสถานะของมันไปยังไฟล์ชั่วคราวและฆ่ากระบวนการ“ ชั่วขณะหลับ”
  • sleep 1 เพื่อป้องกันสภาวะการแย่งชิงที่กระบวนการพื้นหลังแรกตายก่อนที่กระบวนการที่สองจะได้รับโอกาสในการเขียน PID ลงในไฟล์
  • รอให้กระบวนการ“ นอนหลับตลอดไป” สิ้นสุดลง นี้เกิดขึ้นหลังจากที่ทั้งสองของgulpการออกจากกระบวนการตามที่ระบุไว้ข้างต้น
  • ดูว่ากระบวนการพื้นหลังใดถูกยกเลิก หากล้มเหลวให้ฆ่าอีกตัว
  • หากกระบวนการหนึ่งล้มเหลวและเราฆ่าอีกกระบวนการหนึ่งให้รอกระบวนการที่สองเพื่อสรุปและบันทึกสถานะของไฟล์ไว้ในไฟล์ หากกระบวนการแรกเสร็จสมบูรณ์ให้รอจนกว่ากระบวนการที่สองจะเสร็จสิ้น
  • ตรวจสอบสถานะของทั้งสองกระบวนการ

1

เพื่อความสมบูรณ์นี่คือสิ่งที่ฉันใช้:

#!/bin/bash
(cd frontend && gulp serve) &
(cd backend && gulp serve --verbose) &
wait -n
kill 0

สิ่งนี้ใช้ได้กับฉันใน Git สำหรับ Windows 2.5.3 64 บิต รุ่นเก่าอาจไม่ยอมรับตัวเลือก-nwait


1

ในระบบของฉัน (Centos) waitไม่มี-nดังนั้นฉันทำสิ่งนี้:

{ sleep 3; echo one;  } &
FOO=$!
{ sleep 6; echo two;  } &
wait $FOO
pkill -P $$

สิ่งนี้จะไม่รอ "ทั้งสอง" แทนที่จะรอเป็นคนแรก แต่ก็ยังสามารถช่วยถ้าคุณรู้ว่าเซิร์ฟเวอร์จะหยุดก่อน


ไม่ว่าจะwaitมี-nตัวเลือกหรือไม่ขึ้นอยู่กับเปลือกที่คุณใช้ไม่กระจาย Linux
Kusalananda
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.