จะรับ ID กระบวนการของกระบวนการพื้นหลังได้อย่างไร


375

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

วิธีรับ PID ของกระบวนการนี้จากเชลล์สคริปต์ของฉัน เท่าที่ฉันเห็นตัวแปร$!มี PID ของสคริปต์ปัจจุบันไม่ใช่กระบวนการพื้นหลัง


8
$! ถูกต้อง. คุณแน่ใจหรือว่าคุณเริ่มต้นสคริปต์ใน BG ตัวอย่างได้โปรด
pixelbeat

7
ใช่ $! ถูกต้อง. ฉันผิดไป.
Volodymyr Bezuglyy

11
$$ มี PID ของสคริปต์ปัจจุบัน
HUB

คำตอบ:


583

คุณต้องบันทึก PID ของกระบวนการพื้นหลังในเวลาที่คุณเริ่ม:

foo &
FOO_PID=$!
# do other stuff
kill $FOO_PID

คุณไม่สามารถใช้การควบคุมงานได้เนื่องจากเป็นคุณลักษณะแบบโต้ตอบและเชื่อมโยงกับเทอร์มินัลการควบคุม สคริปต์ไม่จำเป็นต้องมีเทอร์มินัลติดอยู่เลยดังนั้นการควบคุมงานจะไม่สามารถใช้ได้


22
ตั้งแต่ $! ส่งคืน pid ของกระบวนการพื้นหลังล่าสุด เป็นไปได้ไหมว่ามีบางอย่างเริ่มต้นระหว่างfooและ$!เราได้อะไรบางอย่างจาก pid แทนที่จะfooเป็น?
WiSaGaN

53
@WiSaGaN: ไม่ไม่มีอะไรระหว่างบรรทัดเหล่านั้น กิจกรรมอื่น ๆ ในระบบจะไม่ส่งผลกระทบต่อสิ่งนี้ $! จะขยายเป็น PID ของกระบวนการพื้นหลังสุดท้ายในเชลล์นั้น
camh

8
... ซึ่งจะทำให้คุณเสียท่อหากfooมีคำสั่งหลาย piped (เช่นtail -f somefile.txt | grep sometext) ในกรณีเช่นนี้คุณจะได้รับ PID ของคำสั่ง grep $!มากกว่าคำสั่ง tail หากนั่นคือสิ่งที่คุณต้องการ คุณจะต้องใช้jobsหรือpsชอบในกรณีนี้
John Rix

1
@JohnRix: ไม่จำเป็น คุณจะได้รับ pid ของgrepแต่ถ้าคุณฆ่าที่หางจะได้รับ SIGPIPE เมื่อมันพยายามที่จะเขียนไปยังท่อ แต่ทันทีที่คุณพยายามเข้าสู่การจัดการ / ควบคุมกระบวนการที่ยุ่งยาก bash / shell จะค่อนข้างเจ็บปวด
camh

5
วิธีการแก้ปัญหาที่เพิ่งเริ่มต้น : โอ้และ "oneliner": /bin/sh -c 'echo $$>/tmp/my.pid && exec program args' &- sysfault 24 พ.ย. 53 เวลา 14:28
imz - Ivan Zakharyaschev

148

คุณสามารถใช้jobs -lคำสั่งเพื่อไปที่ jobL เฉพาะ

^Z
[1]+  Stopped                 guard

my_mac:workspace r$ jobs -l
[1]+ 46841 Suspended: 18           guard

ในกรณีนี้ 46841 เป็น PID

จากhelp jobs:

-l รายงาน ID กลุ่มกระบวนการและไดเรกทอรีการทำงานของงาน

jobs -p เป็นอีกตัวเลือกหนึ่งที่แสดงเฉพาะ PID


หากต้องการใช้สิ่งนี้ในเชลล์สคริปต์คุณจะต้องประมวลผลเอาต์พุต
ฟิล

6
@Phil เพื่อแสดงรายการ pids เท่านั้น: jobs -p. ในการแสดงรายการ pid ของงานที่ต้องการ: jobs -p% 3 ไม่จำเป็นต้องประมวลผลเอาต์พุต
Erik Aronesty

1
@Erik พร้อมคำสั่ง / อาร์กิวเมนต์ที่แตกต่างกันคุณเปลี่ยนบริบทของความคิดเห็นของฉัน หากไม่มีอาร์กิวเมนต์เพิ่มเติมแนะนำว่าเอาต์พุตต้องการการประมวลผล แนะนำการปรับปรุงคำตอบ!
Phil

ประหยัด PID จาก$!หลังจากที่คุณเริ่มมันเป็นแบบพกพาและตรงไปตรงมาในสถานการณ์ส่วนใหญ่ นั่นคือสิ่งที่คำตอบที่ยอมรับในปัจจุบันทำ
tripleee

jobs -pกลับเช่นเดียวกับjobs -lใน Lubuntu 16.4
Timo

46
  • $$ เป็น pid ของสคริปต์ปัจจุบัน
  • $! เป็น pid ของกระบวนการพื้นหลังที่ผ่านมา

นี่คือตัวอย่างการถอดเสียงจากเซสชัน bash ( %1หมายถึงหมายเลขลำดับของกระบวนการพื้นหลังตามที่เห็นจากjobs):

$ echo $$
3748

$ sleep 100 &
[1] 192

$ echo $!
192

$ kill %1

[1]+  Terminated              sleep 100

echo %1ไม่ส่งคืนกระบวนการพื้นหลังบน Ubuntu ของฉันในขณะที่echo $!ทำ
Timo

25

วิธีที่ง่ายยิ่งขึ้นในการฆ่ากระบวนการลูกทั้งหมดของสคริปต์ทุบตี:

pkill -P $$

การ-Pตั้งค่าสถานะทำงานในลักษณะเดียวกันกับpkillและpgrep- รับกระบวนการลูกโดยเฉพาะกับpkillกระบวนการลูกถูกฆ่าและpgrepพิมพ์รหัส PID ให้กับ stdout


สะดวกมาก! นี่เป็นวิธีที่ดีที่สุดเพื่อให้แน่ใจว่าคุณจะไม่ปล่อยให้กระบวนการที่เปิดอยู่บนพื้นหลัง
lepe

@lepe: ไม่มาก หากคุณเป็นปู่ย่าตายายสิ่งนี้จะไม่ทำงาน: หลังจากการbash -c 'bash -c "sleep 300 &"' & วิ่งpgrep -P $$ไม่แสดงอะไรเลยเพราะการนอนหลับจะไม่เป็นลูกโดยตรงของเปลือกหอยของคุณ
ทำให้กลัว

1
@AlexeyPolonsky: มันควรจะเป็น: ฆ่า procs ลูกทั้งหมดของเชลล์ไม่ใช่สคริปต์ เพราะ$$อ้างถึงเปลือกปัจจุบัน
Timo

การแสดงbash -c 'bash -c "sleep 300 &"' & ; pgrep -P $$ฉันไปถึง stdout [1] <pid>. So at least it shows something, but this is probably not the output of pgrep`
Timo

แม้แต่กระบวนการก็สามารถดึงออกจากกระบวนการหลักได้ เคล็ดลับง่ายๆคือการเรียกส้อม (สร้างหลาน) แล้วปล่อยให้กระบวนการลูกออกในขณะที่หลานยังคงทำงานต่อไป (daemonization เป็นคำสำคัญ) แต่แม้ว่าเด็ก ๆ จะยังคงทำงาน pkill -P ไม่เพียงพอที่จะถ่ายทอดสัญญาณให้หลาน ๆ ต้องใช้เครื่องมืออย่าง pstree เพื่อติดตามทรีกระบวนการทั้งหมด แต่สิ่งนี้จะไม่จับ daemons ที่เริ่มต้นจากกระบวนการเนื่องจากพ่อแม่ของพวกเขาเป็นกระบวนการ 1 เช่น:bash -c 'bash -c "sleep 10 & wait $!"' & sleep 0.1; pstree -p $$
Pauli Nieminen

4

นี่คือสิ่งที่ฉันได้ทำ ลองดูสิหวังว่ามันจะช่วยได้

#!/bin/bash
#
# So something to show.
echo "UNO" >  UNO.txt
echo "DOS" >  DOS.txt
#
# Initialize Pid List
dPidLst=""
#
# Generate background processes
tail -f UNO.txt&
dPidLst="$dPidLst $!"
tail -f DOS.txt&
dPidLst="$dPidLst $!"
#
# Report process IDs
echo PID=$$
echo dPidLst=$dPidLst
#
# Show process on current shell
ps -f
#
# Start killing background processes from list
for dPid in $dPidLst
do
        echo killing $dPid. Process is still there.
        ps | grep $dPid
        kill $dPid
        ps | grep $dPid
        echo Just ran "'"ps"'" command, $dPid must not show again.
done

จากนั้นเพียงแค่เรียกใช้เป็น: ./bgkill.shด้วยสิทธิ์ที่เหมาะสมแน่นอน

root@umsstd22 [P]:~# ./bgkill.sh
PID=23757
dPidLst= 23758 23759
UNO
DOS
UID        PID  PPID  C STIME TTY          TIME CMD
root      3937  3935  0 11:07 pts/5    00:00:00 -bash
root     23757  3937  0 11:55 pts/5    00:00:00 /bin/bash ./bgkill.sh
root     23758 23757  0 11:55 pts/5    00:00:00 tail -f UNO.txt
root     23759 23757  0 11:55 pts/5    00:00:00 tail -f DOS.txt
root     23760 23757  0 11:55 pts/5    00:00:00 ps -f
killing 23758. Process is still there.
23758 pts/5    00:00:00 tail
./bgkill.sh: line 24: 23758 Terminated              tail -f UNO.txt
Just ran 'ps' command, 23758 must not show again.
killing 23759. Process is still there.
23759 pts/5    00:00:00 tail
./bgkill.sh: line 24: 23759 Terminated              tail -f DOS.txt
Just ran 'ps' command, 23759 must not show again.
root@umsstd22 [P]:~# ps -f
UID        PID  PPID  C STIME TTY          TIME CMD
root      3937  3935  0 11:07 pts/5    00:00:00 -bash
root     24200  3937  0 11:56 pts/5    00:00:00 ps -f

3

คุณอาจใช้ pstree:

pstree -p user

โดยทั่วไปจะให้การแสดงข้อความของกระบวนการทั้งหมดสำหรับ "ผู้ใช้" และตัวเลือก -p ให้กระบวนการ -ID มันไม่ได้ขึ้นอยู่กับที่ฉันเข้าใจว่าการมีโปรเซสนั้นเป็นของเชลล์ปัจจุบัน นอกจากนี้ยังแสดงให้เห็นถึงส้อม


1
หากต้องการใช้สิ่งนี้ในเชลล์สคริปต์คุณจะต้องประมวลผลเอาต์พุตอย่างหนัก
ฟิล

1

pgrepสามารถให้คุณ PID ​​ลูกทั้งหมดของกระบวนการผู้ปกครอง ดังที่ได้กล่าวไว้ก่อนหน้านี้$$คือ PID ของสคริปต์ปัจจุบัน ดังนั้นหากคุณต้องการสคริปต์ที่ล้างออกหลังจากนี้ควรทำเคล็ดลับ:

trap 'kill $( pgrep -P $$ | tr "\n" " " )' SIGINT SIGTERM EXIT

สิ่งนี้จะไม่ฆ่าล็อตหรือไม่
ฟิล

ใช่มันเป็นคำถามที่ไม่เคยพูดถึงการรักษาเด็กพื้นหลังบางส่วนที่มีชีวิตอยู่เมื่อออกจาก
errant.info

trap 'pkill -P $$' SIGING SIGTERM EXITดูง่ายกว่า แต่ฉันไม่ได้ทดสอบ
ทำให้กลัว

เพื่อความเข้ากันได้อย่าใช้ส่วนSIGนำหน้า มันได้รับอนุญาตจาก POSIX แต่เป็นส่วนขยายที่การใช้งานอาจสนับสนุน: pubs.opengroup.org/onlinepubs/007904975/utilities/trap.htmlเปลือก dash ตัวอย่างเช่นไม่
josch
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.