รอให้กระบวนการเสร็จสิ้น


147

มีคุณสมบัติในตัวใน Bash ที่จะรอให้กระบวนการเสร็จสิ้นหรือไม่?

waitคำสั่งอนุญาตให้ใช้เพียงอย่างใดอย่างหนึ่งที่จะรอให้กระบวนการที่เด็กจะเสร็จสิ้น ฉันต้องการทราบว่ามีวิธีใดบ้างที่จะรอให้กระบวนการเสร็จสิ้นก่อนที่จะดำเนินการกับสคริปต์ใด ๆ

วิธีการเชิงกลในการทำเช่นนี้มีดังต่อไปนี้ แต่ฉันต้องการที่จะทราบว่ามีคุณสมบัติในตัวใน Bash

while ps -p `cat $PID_FILE` > /dev/null; do sleep 1; done

4
ให้ฉันให้ข้อควรระวังสองข้อ : 1. ตามที่ระบุไว้ด้านล่างโดยmp3foley "kill -0" จะไม่ทำงานสำหรับ POSIX เสมอไป 2. อาจเป็นไปได้ว่าคุณต้องการให้แน่ใจว่ากระบวนการไม่ใช่ซอมบี้ซึ่งเป็นกระบวนการที่ถูกยกเลิก ดู ความคิดเห็นของ mp3foleyและเหมืองเพื่อดูรายละเอียด
teika kazura

2
ข้อควรระวังอีกประการ ( แต่เดิมชี้ให้เห็นโดยks1322ด้านล่าง): การใช้ PID นอกเหนือจากกระบวนการลูกไม่แข็งแรง หากคุณต้องการวิธีที่ปลอดภัยให้ใช้เช่น IPC
teika kazura

คำตอบ:


139

เพื่อรอให้กระบวนการใด ๆ เสร็จสิ้น

ลินุกซ์:

tail --pid=$pid -f /dev/null

ดาร์วิน (ต้อง$pidมีไฟล์ที่เปิด):

lsof -p $pid +r 1 &>/dev/null

ด้วยการหมดเวลา (วินาที)

ลินุกซ์:

timeout $timeout tail --pid=$pid -f /dev/null

ดาร์วิน (ต้อง$pidมีไฟล์ที่เปิด):

lsof -p $pid +r 1m%s -t | grep -qm1 $(date -v+${timeout}S +%s 2>/dev/null || echo INF)

43
ใครจะรู้ว่าtailจะทำเช่นนี้
ctrl-alt-delor

8
tailทำงานภายใต้ประทุนโดยการทำโพลกับkill(pid, SIG_0)โพรเซส (ค้นพบโดยใช้strace)
Att Righ

2
โปรดทราบว่า lsof ใช้การสำรวจความคิดเห็นนั่น+r 1คือหมดเวลาฉันกำลังมองหาวิธีการแก้ปัญหาสำหรับ MacOS ที่ไม่ได้ใช้การเลือกตั้ง
Alexander Mills

1
เคล็ดลับนี้ล้มเหลวสำหรับซอมบี้ มันก็โอเคสำหรับกระบวนการที่คุณไม่สามารถฆ่าได้ มีสายtail kill (pid, 0) != 0 && errno != EPERM
teika kazura

2
@AlexanderMills หากคุณสามารถทนต่อระบบ macOS ของคุณไม่ให้เข้าสู่โหมดสลีปขณะที่คำสั่งดำเนินการcaffeinate -w $pidจะทำเคล็ดลับ
zneak

83

ไม่มีบิวอิน ใช้kill -0ในการวนซ้ำสำหรับโซลูชันที่ใช้การได้:

anywait(){

    for pid in "$@"; do
        while kill -0 "$pid"; do
            sleep 0.5
        done
    done
}

หรือเป็นผู้เผยแพร่บนอินเทอร์เน็ตที่ง่ายขึ้นสำหรับการใช้งานครั้งเดียวง่าย:

while kill -0 PIDS 2> /dev/null; do sleep 1; done;

ตามที่ระบุไว้โดยผู้แสดงความคิดเห็นหลายคนหากคุณต้องการรอกระบวนการที่คุณไม่มีสิทธิ์ในการส่งสัญญาณคุณมีวิธีอื่นในการตรวจสอบว่ากระบวนการกำลังทำงานเพื่อแทนที่การkill -0 $pidโทร บน Linux test -d "/proc/$pid"ทำงานได้บนระบบอื่น ๆ ที่คุณอาจต้องใช้pgrep(ถ้ามี) หรืออะไรทำนองps | grep "^$pid "นั้น


2
ข้อควรระวัง : นี้ไม่เคยทำงานเป็นออกแหลมด้านล่างโดยmp3foley ดูความคิดเห็นที่และเหมืองเพื่อดูรายละเอียด
teika kazura

2
ข้อควรระวัง 2 (สำหรับซอมบี้): ความคิดเห็นที่ติดตามโดยเท็ดดี้ด้านบนยังไม่เพียงพอเนื่องจากอาจเป็นซอมบี้ ดูคำตอบของฉันด้านล่างสำหรับโซลูชัน Linux
teika kazura

4
โซลูชันนี้ไม่เสี่ยงต่อสภาพการแข่งขันหรือไม่ ขณะนอนหลับอยู่ในsleep 0.5กระบวนการที่มีอาจจะตายและกระบวนการอื่นอาจถูกสร้างขึ้นด้วยเหมือนกัน$pid $pidและเราจะสิ้นสุดการรอคอยสำหรับ 2 กระบวนการที่แตกต่างกัน (หรือมากกว่า) $pidด้วยเหมือนกัน
ks1322

2
@ ks1322 ใช่รหัสนี้แน่นอนมีสภาพการแข่งขันในนั้น
ตุ๊กตา

4
PID มักจะไม่สร้างขึ้นตามลำดับหรือไม่ ความน่าจะเป็นของการนับรอบในหนึ่งวินาทีคืออะไร?
esmiralha

53

ฉันพบ "kill -0" ไม่ทำงานหากกระบวนการเป็นเจ้าของโดย root (หรืออื่น ๆ ) ดังนั้นฉันจึงใช้ pgrep และมาพร้อมกับ:

while pgrep -u root process_name > /dev/null; do sleep 1; done

นี่จะมีข้อเสียของการจับคู่กระบวนการซอมบี้


2
การสังเกตที่ดี ใน POSIX การเรียกระบบkill(pid, sig=0)ล้มเหลวหากกระบวนการผู้โทรไม่มีสิทธิ์ในการฆ่า ดังนั้น / bin / kill -0 และ "kill -0" (bash ในตัว) จึงล้มเหลวภายใต้เงื่อนไขเดียวกัน
teika kazura

31

bash script loop นี้จะจบลงหากกระบวนการไม่มีอยู่หรือเป็นซอมบี้

PID=<pid to watch>
while s=`ps -p $PID -o s=` && [[ "$s" && "$s" != 'Z' ]]; do
    sleep 1
done

แก้ไข : สคริปต์ข้างต้นได้รับด้านล่าง โดยRockallite ขอบคุณ!

คำตอบของฉันด้านล่าง orignal งานสำหรับลินุกซ์อาศัยเช่นprocfs /proc/ฉันไม่ทราบว่าพกพาได้:

while [[ ( -d /proc/$PID ) && ( -z `grep zombie /proc/$PID/status` ) ]]; do
    sleep 1
done

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


1
ทำได้ดีนี่. แม้ว่าฉันจะต้องล้อมรอบgrep /proc/$PID/statusด้วยอัญประกาศคู่ ( bash: test: argument expected)
Griddo

ฮัม ... เพิ่งลองอีกครั้งและใช้งานได้ ฉันเดาว่าฉันทำอะไรผิดครั้งที่แล้ว
Griddo

7
หรือwhile s=`ps -p $PID -o s=` && [[ "$s" && "$s" != 'Z' ]]; do sleep 1; done
Rockallite

1
น่าเสียดายที่นี่ใช้งานไม่ได้กับ BusyBox - ในpsนั้นไม่รองรับ-pหรือไม่s=ได้รับการสนับสนุน
ZimbiX

14

FreeBSD และ Solaris มีpwait(1)ยูทิลิตี้ที่มีประโยชน์นี้ซึ่งทำสิ่งที่คุณต้องการ

ฉันเชื่อว่าระบบปฏิบัติการที่ทันสมัยอื่น ๆ ก็มีการเรียกระบบที่จำเป็นด้วยเช่นกัน (เช่น MacOS, ใช้ BSD's kqueue) แต่ก็ไม่ทั้งหมดทำให้มันพร้อมใช้งานจาก command-line


2
> BSD and Solaris: ตรวจสอบ BSD ขนาดใหญ่ทั้งสามที่อยู่ในใจ ค่า OpenBSD NetBSD มิได้มีฟังก์ชั่นนี้ (ในหน้าคนของพวกเขา) เพียง FreeBSD ไม่เป็นคุณสามารถตรวจสอบได้ที่man.openbsd.org
benaryorg

ดูเหมือนว่าคุณจะถูก Mea culpa ... พวกเขานำไปใช้งานkqueueดังนั้นการรวบรวม FreeBSD pwait(1)จะเป็นเรื่องเล็กน้อย เพราะเหตุใดการนำเข้าของ BSD อื่น ๆ จะไม่ทำให้คุณลักษณะดังกล่าวหนีฉัน ...
มิคาอิลต.

1
plink me@oracle box -pw redacted "pwait 6998";email -b -s "It's done" etcเพิ่งอนุญาตให้ฉันกลับบ้านตอนนี้แทนที่จะเป็นชั่วโมงจากนี้
zzxyz

11

จาก manpage ทุบตี

   wait [n ...]
          Wait for each specified process and return its termination  status
          Each  n  may be a process ID or a job specification; if a
          job spec is given, all processes  in  that  job's  pipeline  are
          waited  for.  If n is not given, all currently active child processes
          are waited for, and the return  status  is  zero.   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.

56
มันเป็นความจริง แต่มันสามารถรอลูกของเปลือกปัจจุบัน คุณไม่สามารถรอกระบวนการใด ๆ
gumik

@gumik: "ถ้า n ไม่ได้รับทุกกระบวนการที่เด็กใช้งานในปัจจุบันมีการรอคอย" การทำงานนี้สมบูรณ์แบบ .. waitโดยไม่มี args จะบล็อกกระบวนการจนกว่ากระบวนการลูกใด ๆจะเสร็จสิ้น สุจริตฉันไม่เห็นจุดที่รอกระบวนการใด ๆเนื่องจากมีการประมวลผลระบบที่เกิดขึ้นเสมอ
coderofsalvation

1
@coderofsalvation (sleep 10 & sleep 3 & wait) ใช้เวลา 10 วินาทีในการส่งคืน: รอโดยไม่มี args จะบล็อกจนกว่ากระบวนการลูกทั้งหมดจะเสร็จสิ้น OP ต้องการรับการแจ้งเตือนเมื่อกระบวนการลูกคนแรก (หรือเสนอชื่อ) เสร็จสิ้น
android.weasel

นอกจากนี้ยังไม่ทำงานหากกระบวนการไม่ได้อยู่ในพื้นหลังหรือเบื้องหน้า (บน Solaris, Linux หรือ Cygwin) อดีต sleep 1000 ctrl-z wait [sleep pid]ส่งคืนทันที
zzxyz

6

โซลูชันเหล่านี้ทั้งหมดได้รับการทดสอบใน Ubuntu 14.04:

โซลูชันที่ 1 (โดยใช้คำสั่ง ps): เพียงเพิ่มคำตอบ Pierz ฉันอยากจะแนะนำ:

while ps axg | grep -vw grep | grep -w process_name > /dev/null; do sleep 1; done

ในกรณีนี้grep -vw grepตรวจสอบให้แน่ใจว่า grep ตรงกับ process_name เท่านั้นและไม่ใช่ grep เอง มันมีความได้เปรียบในการสนับสนุนกรณีที่ process_name ps axgไม่ได้อยู่ที่ท้ายบรรทัดที่ที่

โซลูชันที่ 2 (โดยใช้คำสั่งด้านบนและชื่อกระบวนการ):

while [[ $(awk '$12=="process_name" {print $0}' <(top -n 1 -b)) ]]; do sleep 1; done

แทนที่ด้วยชื่อกระบวนการที่ปรากฏในprocess_name top -n 1 -bโปรดเก็บเครื่องหมายคำพูด

หากต้องการดูรายการกระบวนการที่คุณรอให้กระบวนการเสร็จสิ้นคุณสามารถเรียกใช้:

while : ; do p=$(awk '$12=="process_name" {print $0}' <(top -n 1 -b)); [[ $b ]] || break; echo $p; sleep 1; done

โซลูชัน 3 (โดยใช้คำสั่งด้านบนและ ID กระบวนการ):

while [[ $(awk '$1=="process_id" {print $0}' <(top -n 1 -b)) ]]; do sleep 1; done

แทนที่process_idด้วย ID กระบวนการของโปรแกรมของคุณ


4
Downvote: grep -v grepไปป์ไลน์ที่ยาวเป็น antipattern ขนาดใหญ่และสิ่งนี้อนุมานว่าคุณไม่มีกระบวนการที่ไม่เกี่ยวข้องกับชื่อเดียวกัน หากคุณรู้จัก PID แทนสิ่งนี้สามารถปรับให้เข้ากับวิธีการทำงานที่เหมาะสม
tripleee

ขอบคุณ tripleee สำหรับความคิดเห็น ฉันเพิ่มธง-wเพื่อหลีกเลี่ยงปัญหาในgrep -v grep ระดับหนึ่ง ฉันยังเพิ่มโซลูชันอีกสองรายการตามความคิดเห็นของคุณ
Saeid BK

5

ตกลงดังนั้นดูเหมือนว่าคำตอบคือ - ไม่ไม่มีเครื่องมือในตัว

หลังจากการตั้งค่า/proc/sys/kernel/yama/ptrace_scopeที่จะ0เป็นไปได้ที่จะใช้straceโปรแกรม สวิตช์เพิ่มเติมสามารถนำมาใช้เพื่อทำให้เงียบได้เพื่อให้รออย่างแท้จริง:

strace -qqe '' -p <PID>

1
ทำได้ดีนี่! มันดูเหมือนว่าที่มันเป็นไปไม่ได้ที่จะแนบไปกับ PID รับจากสองสถานที่ที่แตกต่างกัน (ฉันจะได้รับOperation not permittedเช่น strace ที่สอง); คุณยืนยันได้ไหม
eudoxos

@eudoxos ใช่ manpage สำหรับ ptrace พูดว่า: (...)"tracee" always means "(one) thread"(และฉันยืนยันข้อผิดพลาดที่คุณพูดถึง) หากต้องการให้กระบวนการเพิ่มเติมรอด้วยวิธีนี้คุณจะต้องสร้างเครือข่าย
นกอีมู

2

วิธีการแก้ปัญหาการปิดกั้น

ใช้การwaitวนซ้ำเพื่อรอให้ยุติกระบวนการทั้งหมด:

function anywait()
{

    for pid in "$@"
    do
        wait $pid
        echo "Process $pid terminated"
    done
    echo 'All processes terminated'
}

ฟังก์ชั่นนี้จะออกทันทีเมื่อกระบวนการทั้งหมดถูกยกเลิก นี่คือทางออกที่มีประสิทธิภาพที่สุด

โซลูชันที่ไม่ปิดกั้น

ใช้kill -0ในวงวนเพื่อรอให้ยุติกระบวนการทั้งหมด + ทำอะไรก็ได้ระหว่างการตรวจสอบ:

function anywait_w_status()
{
    for pid in "$@"
    do
        while kill -0 "$pid"
        do
            echo "Process $pid still running..."
            sleep 1
        done
    done
    echo 'All processes terminated'
}

เวลาตอบสนองลดลงตามsleepเวลาเนื่องจากต้องป้องกันการใช้งาน CPU สูง

การใช้งานจริง:

รอการยุติกระบวนการทั้งหมด + แจ้งผู้ใช้เกี่ยวกับPID ที่กำลังทำงานอยู่ทั้งหมด

function anywait_w_status2()
{
    while true
    do
        alive_pids=()
        for pid in "$@"
        do
            kill -0 "$pid" 2>/dev/null \
                && alive_pids+="$pid "
        done

        if [ ${#alive_pids[@]} -eq 0 ]
        then
            break
        fi

        echo "Process(es) still running... ${alive_pids[@]}"
        sleep 1
    done
    echo 'All processes terminated'
}

หมายเหตุ

ฟังก์ชั่นเหล่านี้รับ PID ผ่านการขัดแย้งโดย$@เป็นอาร์เรย์ BASH


2

มีปัญหาเดียวกันฉันแก้ไขปัญหาการฆ่ากระบวนการแล้วรอให้กระบวนการแต่ละกระบวนการเสร็จสิ้นโดยใช้ระบบไฟล์ PROC:

while [ -e /proc/${pid} ]; do sleep 0.1; done

การลงคะแนนเลือกตั้งแย่มากคุณสามารถไปพบตำรวจได้ :)
Alexander Mills

2

ไม่มีคุณสมบัติในตัวที่จะรอให้กระบวนการใด ๆ เสร็จสิ้น

คุณสามารถส่งkill -0ไปยัง PID ที่พบดังนั้นคุณจะไม่ได้รับความสับสนจากซอมบี้และสิ่งต่าง ๆ ที่จะยังคงปรากฏอยู่ps(ในขณะที่ยังคงดึงรายการ PID โดยใช้ps)


1

ใช้ inotifywait เพื่อตรวจสอบไฟล์บางไฟล์ที่ถูกปิดเมื่อกระบวนการของคุณสิ้นสุดลง ตัวอย่าง (บน Linux):

yourproc >logfile.log & disown
inotifywait -q -e close logfile.log

-e ระบุเหตุการณ์ที่จะรอ -q หมายถึงเอาต์พุตขั้นต่ำสุดต่อเมื่อสิ้นสุดเท่านั้น ในกรณีนี้มันจะเป็น:

logfile.log CLOSE_WRITE,CLOSE

คำสั่ง wait เดียวสามารถใช้เพื่อรอหลายกระบวนการ:

yourproc1 >logfile1.log & disown
yourproc2 >logfile2.log & disown
yourproc3 >logfile3.log & disown
inotifywait -q -e close logfile1.log logfile2.log logfile3.log

สตริงเอาต์พุตของ inotifywait จะบอกคุณว่ากระบวนการใดถูกยกเลิก ใช้งานได้กับไฟล์ 'ของจริง' เท่านั้นไม่ใช่ใน / proc /


0

ในระบบเช่น OSX คุณอาจไม่มี pgrep ดังนั้นคุณสามารถลองประเมินค่านี้เมื่อค้นหากระบวนการตามชื่อ:

while ps axg | grep process_name$ > /dev/null; do sleep 1; done

$สัญลักษณ์ในตอนท้ายของเพื่อให้แน่ใจชื่อกระบวนการที่ตรงกับ grep เพียง process_name ไปยังจุดสิ้นสุดของเส้นในการส่งออก PS และตัวเองไม่ได้


น่ากลัว: อาจมีหลายกระบวนการที่มีชื่อนั้นอยู่ที่ไหนสักแห่งในบรรทัดคำสั่ง grep ของคุณเองรวมอยู่ด้วย แทนการเปลี่ยนเส้นทางไป/dev/null, ควรจะใช้กับ-q grepตัวอย่างของกระบวนการอื่นอาจได้เริ่มต้นในขณะที่วงของคุณกำลังนอนหลับและคุณจะไม่เคยรู้ ...
มิคาอิลตัน

ฉันไม่แน่ใจว่าทำไมคุณจึงแยกคำตอบนี้เป็น 'น่ากลัว' - เป็นวิธีที่คล้ายกันที่ได้รับการแนะนำโดยผู้อื่น? ในขณะที่-qข้อเสนอแนะนั้นถูกต้องตามที่ฉันกล่าวถึงในคำตอบของฉันโดยเฉพาะ$หมายความว่าการเลิกจ้างgrep จะไม่ตรงกับชื่อ "ที่ไหนสักแห่งในบรรทัดคำสั่ง" หรือจะไม่ตรงกับตัวเอง คุณลองใช้ OSX จริงหรือไม่?
Pierz

0

วิธีแก้ปัญหาของ Rauno Palosaari เป็นวิธีแก้ปัญหาTimeout in Seconds Darwinที่ยอดเยี่ยมสำหรับระบบปฏิบัติการ UNIX ที่ไม่มี GNU tail(ไม่เฉพาะเจาะจงDarwin) แต่ขึ้นอยู่กับอายุของระบบปฏิบัติการคล้าย UNIX บรรทัดคำสั่งที่เสนอมีความซับซ้อนเกินความจำเป็นและอาจล้มเหลว:

lsof -p $pid +r 1m%s -t | grep -qm1 $(date -v+${timeout}S +%s 2>/dev/null || echo INF)

บน UNIX เก่าอย่างน้อยหนึ่งlsofอาร์กิวเมนต์จะ+r 1m%sล้มเหลว (แม้สำหรับ superuser):

lsof: can't read kernel name list.

m%sเป็นข้อกำหนดรูปแบบการออก โพสต์โปรเซสเซอร์ที่เรียบง่ายไม่จำเป็นต้องใช้ ตัวอย่างเช่นคำสั่งต่อไปนี้รอบน PID 5959 นานถึงห้าวินาที:

lsof -p 5959 +r 1 | awk '/^=/ { if (T++ >= 5) { exit 1 } }'

ในตัวอย่างนี้ถ้า PID 5959 ออกจากสอดคล้องของตัวเองก่อนที่ห้าวินาทีผ่านไปเป็น${?} 0ถ้าไม่${?}กลับมา1หลังจากห้าวินาที

มันอาจจะมีมูลค่า noting ชัดแจ้งในที่+r 1นี้1เป็นช่วงโพล (วินาที) ดังนั้นจึงอาจมีการเปลี่ยนแปลงเพื่อให้เหมาะกับสถานการณ์

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