หากกระบวนการลูกที่เกิดใหม่ล้มเหลวให้ฆ่าทั้งหมดและออก


9

ในสคริปต์ของฉันฉันแบ่งชุดข้อมูลเป็น input_aa, input_ab และอื่น ๆ จากนั้นฉันเรียกใช้แต่ละสคริปต์ Python เดียวกันเช่น:

# Execute program on each split file
for part in input_*; do
        python3 $part &
done
wait

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

คำตอบ:


10

คุณสามารถใช้กลุ่มกระบวนการ:

set -m
(
   for part in input_*; do
     (python3 "$part" || kill 0) &
   done
   wait
)

set -m(และคุณสมบัติ POSIX เชลล์ที่เป็นทางเลือก, คุณสมบัติ Unix shell ที่จำเป็น) เรียกใช้งานในกลุ่มกระบวนการของตนเอง ในbash, yash, zsh, mkshที่งานของ subshell ที่set -mถูกเปิดใช้งานเพื่อให้ด้านนอก(...)และกระบวนการทั้งหมดที่สร้างขึ้นภายในที่จะถูกวางในการที่กลุ่มกระบวนการเดียวกัน

สำหรับเชลล์ที่มีฐานแบบdashอื่น ๆashใช้งานได้กับกระบวนการเชลล์ระดับบนสุดเท่านั้น ดังนั้นรหัสนั้นจะทำงานเว้นแต่ว่ามันจะใส่ใน subshell

นั่นจะไม่ทำงานใน AT&T kshหรือเชลล์ SysV / Bourne เก่าเลย

kill 0 ส่งสัญญาณ SIGTERM ไปยังสมาชิกทั้งหมดของกลุ่มกระบวนการปัจจุบัน


ในทุบตี ทำไมฉันรวม Shebang ไว้ด้วย - เชลล์ที่ต้องการไม่ชัดเจน คำตอบที่ดี
jim mcnamara

@jimmcnamara ว่าการงานในbash, dash, yash, ,mksh zshโดยทั่วไปเชลล์ POSIX จะเป็น AT&T ksh set -mis (under-) ที่ระบุใน POSIX แต่เป็นคุณสมบัติเสริม
Stéphane Chazelas

ฉันใช้โซลาริส / bin / sh จะไม่บิน
จิม mcnamara

@ jimmcnamara, no / bin / sh บน Solaris 10 และก่อนหน้านี้คือ Bourne shell (ไม่ใช่ POSIX shell) และวันที่ 11, AT&T ksh อย่างที่ฉันบอกว่ามันใช้งานได้กับ bash, dash, yash, mksh, zsh
Stéphane Chazelas

1
@mikeserv ที่จะ reparent กระบวนการเป็น 1 แต่จะไม่นำมันออกจากกลุ่มกระบวนการ kill 0ฆ่าสมาชิกทั้งหมดของกลุ่มกระบวนการไม่ว่าผู้ปกครองจะเป็นอย่างไร ดูps -jเพื่อดูรหัสกลุ่มกระบวนการ
Stéphane Chazelas

3

นี่คือตัวอย่าง เล่นกับสิ่งนี้ก่อนเพื่อให้ได้สิ่งที่คุณต้องการ มันไม่สามารถทำลายได้มากเท่าที่เป็นอยู่

#!/bin/bash
# Example of killing off all children

> killfile
> outfile.err
kill_em()
{
   echo 'killing all children ' > 2
   while read pid
   do
      kill -0 $pid && kill -9 $pid  # if still running kill it
   done < killfile
   exit 1
}

export grandparentpid=$$
trap 'kill_em' 6
for i in 2 2 3 4 5 6 7 8 9 10
do
        ( sleep $i && ls oinkle  >> outfile 2>> outfile.err &
          pid=$!
          echo $pid >> killfile
          wait $!
          [ $? -ne 0 ] && kill -6 $grandparentpid
        ) &
done
wait

การตั้งค่านี้ล้มเหลวโดยเจตนาเพราะls oinkleจะล้มเหลว (บนเครื่องของฉัน)

เมื่อคุณได้รับสิ่งที่คุณต้องการหลังจากแก้ไขสคริปต์เริ่มต้น --- เปลี่ยน:

for i in 2 2 3 4 5 6 7 8 9 10

ถึง:

for part in input_* 

เปลี่ยน:

sleep $i && ls oinkle 

ถึง:

python3 $part 

การเปลี่ยนเส้นทางจะมีการบันทึกบันทึก คุณอาจไม่ต้องการพวกเขา


มันค่อนข้างมีชีวิตชีวา หากงานใดงานหนึ่งล้มเหลวก่อนที่งานอื่นทั้งหมดจะเริ่มต้นคุณkillfileอาจไม่ได้รวมงานทั้งหมดที่เริ่มต้นแล้ว
Stéphane Chazelas

แนวทางปฏิบัติที่ไม่ดีเช่น: ตัวแปรที่ไม่มีเครื่องหมายการใช้หมายเลขสัญญาณแทนชื่อใช้สัญญาณ 6 (เช่น ABRT บน Linux amd64 เป็นต้น) แทน USR1 / USR2 เป็นสัญญาณผู้ใช้[ $? -ne 0 ]...
Stéphane Chazelas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.