ฉันต้องการฆ่าต้นไม้กระบวนการทั้งหมด วิธีที่ดีที่สุดในการทำสิ่งนี้โดยใช้ภาษาสคริปต์ทั่วไปคืออะไร ฉันกำลังมองหาทางออกที่ง่าย
chronos
หรือ herodes
ฉันต้องการฆ่าต้นไม้กระบวนการทั้งหมด วิธีที่ดีที่สุดในการทำสิ่งนี้โดยใช้ภาษาสคริปต์ทั่วไปคืออะไร ฉันกำลังมองหาทางออกที่ง่าย
chronos
หรือ herodes
คำตอบ:
คุณไม่ได้พูดว่าทรีที่คุณต้องการฆ่าเป็นกลุ่มกระบวนการเดียวหรือไม่ (ในกรณีนี้มักจะเกิดจากการฟอร์กจากการเริ่มต้นเซิร์ฟเวอร์หรือบรรทัดคำสั่งเชลล์) คุณสามารถค้นหากลุ่มกระบวนการโดยใช้ GNU ps ดังนี้
ps x -o "%p %r %y %x %c "
หากเป็นกลุ่มกระบวนการที่คุณต้องการฆ่าเพียงใช้kill(1)
คำสั่ง แต่แทนที่จะให้หมายเลขกระบวนการให้ลบค่าหมายเลขกลุ่มนั้น ตัวอย่างที่จะฆ่าทุกขั้นตอนในกลุ่ม 5112, kill -TERM -- -5112
การใช้งาน
pgrep
สามารถเสนอวิธีที่ง่ายกว่าในการค้นหา ID กลุ่มกระบวนการ ตัวอย่างเช่นการฆ่า my-script.sh kill -TERM -$(pgrep -o my-script.sh)
ของกลุ่มกระบวนการการทำงาน
ps -o pid --no-headers --ppid $PARENT_PID
ps x -o "%r %p %y %x %c" | sort -nk1,2
ฆ่ากระบวนการทั้งหมดที่เป็นของแผนผังกระบวนการเดียวกันโดยใช้ID กลุ่มกระบวนการ ( PGID
)
kill -- -$PGID
ใช้สัญญาณเริ่มต้น ( TERM
= 15)kill -9 -$PGID
ใช้สัญญาณKILL
(9)คุณสามารถดึงข้อมูลPGID
จากProcess-IDใด ๆ( PID
) ของแผนผังกระบวนการเดียวกัน
kill -- -$(ps -o pgid= $PID | grep -o '[0-9]*')
(สัญญาณTERM
)kill -9 -$(ps -o pgid= $PID | grep -o '[0-9]*')
(สัญญาณKILL
)ขอขอบคุณเป็นพิเศษสำหรับtanagerและSpeakusสำหรับการสนับสนุน$PID
พื้นที่ที่เหลือและความเข้ากันได้ของ OSX
kill -9 -"$PGID"
=> ส่งสัญญาณ 9 ( KILL
) ไปยังเด็กและหลานทั้งหมด ...PGID=$(ps opgid= "$PID")
=> เรียกกระบวนการกลุ่ม-IDจากกระบวนการ-IDของต้นไม้ที่ไม่เพียง แต่กระบวนการปกครอง-ID รูปแบบของps opgid= $PID
มีที่ps -o pgid --no-headers $PID
ที่จะถูกแทนที่ด้วยpgid
แต่:
pgrp
ps
แทรกช่องว่างนำเมื่อPID
เป็นน้อยกว่าห้าตัวเลขและสอดคล้องขวาสังเกตเห็นโดยTanager คุณสามารถใช้ได้:PGID=$(ps opgid= "$PID" | tr -d ' ')
ps
จาก OSX พิมพ์หัวเรื่องเสมอดังนั้นSpeakusเสนอ:PGID="$( ps -o pgid "$PID" | grep [0-9] | tr -d ' ' )"
grep -o [0-9]*
พิมพ์ตัวเลขต่อเนื่องเท่านั้น (ไม่พิมพ์ช่องว่างหรือส่วนหัวตามตัวอักษร)PGID=$(ps -o pgid= $PID | grep -o [0-9]*)
kill -TERM -"$PGID" # kill -15
kill -INT -"$PGID" # correspond to [CRTL+C] from keyboard
kill -QUIT -"$PGID" # correspond to [CRTL+\] from keyboard
kill -CONT -"$PGID" # restart a stopped process (above signals do not kill it)
sleep 2 # wait terminate process (more time if required)
kill -KILL -"$PGID" # kill -9 if it does not intercept signals (or buggy)
kill
ถูกเรียกใช้โดยกระบวนการที่เป็นของต้นไม้ต้นเดียวกันkill
เสี่ยงต่อการฆ่าตัวตายก่อนที่จะยุติการฆ่าต้นไม้ทั้งหมด> cat run-many-processes.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
./child.sh background &
./child.sh foreground
echo "ProcessID=$$ ends ($0)"
> cat child.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
./grandchild.sh background &
./grandchild.sh foreground
echo "ProcessID=$$ ends ($0)"
> cat grandchild.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
sleep 9999
echo "ProcessID=$$ ends ($0)"
เรียกใช้แผนผังกระบวนการในพื้นหลังโดยใช้ '&'
> ./run-many-processes.sh &
ProcessID=28957 begins (./run-many-processes.sh)
ProcessID=28959 begins (./child.sh)
ProcessID=28958 begins (./child.sh)
ProcessID=28960 begins (./grandchild.sh)
ProcessID=28961 begins (./grandchild.sh)
ProcessID=28962 begins (./grandchild.sh)
ProcessID=28963 begins (./grandchild.sh)
> PID=$! # get the Parent Process ID
> PGID=$(ps opgid= "$PID") # get the Process Group ID
> ps fj
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
28348 28349 28349 28349 pts/3 28969 Ss 33021 0:00 -bash
28349 28957 28957 28349 pts/3 28969 S 33021 0:00 \_ /bin/sh ./run-many-processes.sh
28957 28958 28957 28349 pts/3 28969 S 33021 0:00 | \_ /bin/sh ./child.sh background
28958 28961 28957 28349 pts/3 28969 S 33021 0:00 | | \_ /bin/sh ./grandchild.sh background
28961 28965 28957 28349 pts/3 28969 S 33021 0:00 | | | \_ sleep 9999
28958 28963 28957 28349 pts/3 28969 S 33021 0:00 | | \_ /bin/sh ./grandchild.sh foreground
28963 28967 28957 28349 pts/3 28969 S 33021 0:00 | | \_ sleep 9999
28957 28959 28957 28349 pts/3 28969 S 33021 0:00 | \_ /bin/sh ./child.sh foreground
28959 28960 28957 28349 pts/3 28969 S 33021 0:00 | \_ /bin/sh ./grandchild.sh background
28960 28964 28957 28349 pts/3 28969 S 33021 0:00 | | \_ sleep 9999
28959 28962 28957 28349 pts/3 28969 S 33021 0:00 | \_ /bin/sh ./grandchild.sh foreground
28962 28966 28957 28349 pts/3 28969 S 33021 0:00 | \_ sleep 9999
28349 28969 28969 28349 pts/3 28969 R+ 33021 0:00 \_ ps fj
คำสั่งpkill -P $PID
ไม่ฆ่าหลาน:
> pkill -P "$PID"
./run-many-processes.sh: line 4: 28958 Terminated ./child.sh background
./run-many-processes.sh: line 4: 28959 Terminated ./child.sh foreground
ProcessID=28957 ends (./run-many-processes.sh)
[1]+ Done ./run-many-processes.sh
> ps fj
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
28348 28349 28349 28349 pts/3 28987 Ss 33021 0:00 -bash
28349 28987 28987 28349 pts/3 28987 R+ 33021 0:00 \_ ps fj
1 28963 28957 28349 pts/3 28987 S 33021 0:00 /bin/sh ./grandchild.sh foreground
28963 28967 28957 28349 pts/3 28987 S 33021 0:00 \_ sleep 9999
1 28962 28957 28349 pts/3 28987 S 33021 0:00 /bin/sh ./grandchild.sh foreground
28962 28966 28957 28349 pts/3 28987 S 33021 0:00 \_ sleep 9999
1 28961 28957 28349 pts/3 28987 S 33021 0:00 /bin/sh ./grandchild.sh background
28961 28965 28957 28349 pts/3 28987 S 33021 0:00 \_ sleep 9999
1 28960 28957 28349 pts/3 28987 S 33021 0:00 /bin/sh ./grandchild.sh background
28960 28964 28957 28349 pts/3 28987 S 33021 0:00 \_ sleep 9999
คำสั่งkill -- -$PGID
ฆ่ากระบวนการทั้งหมดรวมถึงหลาน
> kill -- -"$PGID" # default signal is TERM (kill -15)
> kill -CONT -"$PGID" # awake stopped processes
> kill -KILL -"$PGID" # kill -9 to be sure
> ps fj
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
28348 28349 28349 28349 pts/3 29039 Ss 33021 0:00 -bash
28349 29039 29039 28349 pts/3 29039 R+ 33021 0:00 \_ ps fj
ฉันสังเกตเห็นในตัวอย่างนี้PID
และPGID
มีค่าเท่ากัน ( 28957
)
นี่คือเหตุผลที่ฉันคิดว่าเดิมkill -- -$PID
เพียงพอ แต่ในกรณีที่กระบวนการจะวางไข่ภายในหมายเลขกระบวนการจะแตกต่างจากรหัสกลุ่มMakefile
ฉันคิดว่าkill -- -$(ps -o pgid= $PID | grep -o [0-9]*)
เป็นเคล็ดลับง่ายๆที่ดีที่สุดในการฆ่าแผนผังกระบวนการทั้งหมดเมื่อถูกเรียกจากID กลุ่มอื่น (ต้นไม้กระบวนการอื่น)
kill
ควรส่งสัญญาณไปยังต้นไม้ทั้งหมดก่อนที่จะรับสัญญาณของมันเอง แต่ในบางสถานการณ์ / การใช้งานเฉพาะkill
อาจส่งสัญญาณให้กับตัวเองถูกขัดจังหวะแล้วรับสัญญาณของตัวเอง อย่างไรก็ตามความเสี่ยงควรมีน้อยที่สุดและอาจถูกเพิกเฉยในกรณีส่วนใหญ่เนื่องจากข้อบกพร่องอื่น ๆ ควรเกิดขึ้นก่อนหน้านี้ ความเสี่ยงนี้สามารถถูกละเว้นในกรณีของคุณ? นอกจากนี้คำตอบอื่น ๆ ยังมีบั๊กทั่วไปนี้ ( kill
ส่วนหนึ่งของแผนผังกระบวนการที่ถูกฆ่า) หวัง
man
ทำเช่นนั้น ในทางตรงกันข้ามถ้าคุณต้องการฆ่ากระบวนการ gandchild จากกระบวนการลูกkill -- -$pid
จะไม่ทำงาน ดังนั้นจึงไม่ใช่ทางออกทั่วไป
child.sh
พยายามที่จะฆ่าลำดับชั้นของกระบวนการพื้นหลังใน
pkill -TERM -P 27888
นี่จะฆ่ากระบวนการทั้งหมดที่มีกระบวนการหลัก ID 27888
หรือแข็งแกร่งกว่านี้:
CPIDS=$(pgrep -P 27888); (sleep 33 && kill -KILL $CPIDS &); kill -TERM $CPIDS
ซึ่งกำหนดการฆ่าใน 33 วินาทีต่อมาและขอให้กระบวนการยุติอย่างสุภาพ
ดูคำตอบนี้เพื่อยกเลิกลูกหลานทั้งหมด
pkill -TERM -P ${$}
จากสคริปต์ทุบตีเพื่อจะฆ่าลูกของตัวเองของคุณใช้
$ time for i in {1..32768}; do ( echo $BASHPID >> pids ); done real 0m18.860s
เพื่อฆ่าต้นไม้กระบวนการซ้ำใช้ killtree ():
#!/bin/bash
killtree() {
local _pid=$1
local _sig=${2:--TERM}
kill -stop ${_pid} # needed to stop quickly forking parent from producing children between child killing and parent killing
for _child in $(ps -o pid --no-headers --ppid ${_pid}); do
killtree ${_child} ${_sig}
done
kill -${_sig} ${_pid}
}
if [ $# -eq 0 -o $# -gt 2 ]; then
echo "Usage: $(basename $0) <pid> [signal]"
exit 1
fi
killtree $@
--
ข้อโต้แย้งที่จะps
ไม่ทำงานบน OS X ได้จะทำให้การทำงานมีแทนที่ps
คำสั่งโดย: ps ax -o "pid= ppid=" | grep -E "${_regex}" | sed -E "s/${_regex}/\1/g
ที่_regex
ถูกกำหนดไว้ก่อนที่จะfor
ห่วง:local _regex="[ ]*([0-9]+)[ ]+${_pid}"
killtree()
ทำงานได้อย่างน่าเชื่อถือ?
ps
ไม่รองรับ--ppid
ก็สามารถใช้pgrep -P ${_pid}
แทนได้
คำตอบของแบรดคือสิ่งที่ผมอยากแนะนำให้เกินไปยกเว้นว่าคุณสามารถทำไปด้วยawk
โดยสิ้นเชิงถ้าคุณใช้ตัวเลือกในการ--ppid
ps
for child in $(ps -o pid -ax --ppid $PPID) do ....... done
ถ้าคุณรู้ว่าผ่าน pid ของกระบวนการพาเรนต์นี่คือเชลล์สคริปต์ที่ควรใช้:
for child in $(ps -o pid,ppid -ax | \
awk "{ if ( \$2 == $pid ) { print \$1 }}")
do
echo "Killing child process $child because ppid = $pid"
kill $child
done
ฉันใช้วิธีการแก้ไขที่อธิบายไว้ที่นี่: https://stackoverflow.com/a/5311362/563175
ดังนั้นดูเหมือนว่า:
kill `pstree -p 24901 | sed 's/(/\n(/g' | grep '(' | sed 's/(\(.*\)).*/\1/' | tr "\n" " "`
ที่ 24901 เป็น PID ของผู้ปกครอง
มันดูน่าเกลียด แต่มันทำงานได้อย่างสมบูรณ์
pstree -p 24901 | grep -oP '(?<=\()[0-9]+(?=\))'
-l
การpstree
เพื่อให้เส้นยาวไม่ได้รับการตัดทอน; มันสามารถทำให้อ่านง่ายขึ้นด้วยkill `pstree -l -p 24901 |grep "([[:digit:]]*)" -o |tr -d '()'`
(ไม่จำเป็นต้องแปลง\n
เป็นพื้นที่เพราะมันจะทำงานได้ดี), ขอบคุณ!
คำตอบของ zhigang รุ่นที่แก้ไขแล้ว:
#!/usr/bin/env bash
set -eu
killtree() {
local pid
for pid; do
kill -stop $pid
local cpid
for cpid in $(pgrep -P $pid); do
killtree $cpid
done
kill $pid
kill -cont $pid
wait $pid 2>/dev/null || true
done
}
cpids() {
local pid=$1 options=${2:-} space=${3:-}
local cpid
for cpid in $(pgrep -P $pid); do
echo "$space$cpid"
if [[ "${options/a/}" != "$options" ]]; then
cpids $cpid "$options" "$space "
fi
done
}
while true; do sleep 1; done &
cpid=$!
for i in $(seq 1 2); do
cpids $$ a
sleep 1
done
killtree $cpid
echo ---
cpids $$ a
wait $pid
เฉพาะกระบวนการที่คุณเริ่มต้นเท่านั้นไม่ใช่กระบวนการก่อนทั้งหมดดังนั้นนี่ไม่ใช่วิธีแก้ปัญหาทั่วไป
wait
จะระงับTerminated
ข้อความถ้าเป็นเด็ก
ฉันไม่สามารถแสดงความคิดเห็น (มีชื่อเสียงไม่เพียงพอ) ดังนั้นฉันถูกบังคับให้เพิ่มคำตอบใหม่แม้ว่านี่จะไม่ใช่คำตอบจริงๆ
มีปัญหาเล็กน้อยกับคำตอบที่ดีมากและละเอียดโดย @olibre เมื่อวันที่ 28 ก.พ. ผลลัพธ์ของps opgid= $PID
จะมีช่องว่างนำหน้าสำหรับ PID ที่สั้นกว่าห้าหลักเพราะps
จัดชิดคอลัมน์ (จัดตำแหน่งตัวเลขให้ตรง) ภายในบรรทัดคำสั่งทั้งหมดผลลัพธ์จะมีเครื่องหมายลบตามด้วยช่องว่างตามด้วยกลุ่ม PID วิธีง่ายๆคือการท่อps
เพื่อtr
ไปที่ช่องว่างลบ:
kill -- -$( ps opgid= $PID | tr -d ' ' )
เพื่อเพิ่มคำตอบของนอร์แมนแรมซีย์มันอาจคุ้มค่าที่จะดู setsid ถ้าคุณต้องการสร้างกลุ่มกระบวนการ
http://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html
ฟังก์ชัน setsid () จะสร้างเซสชันใหม่หากกระบวนการเรียกใช้ไม่ใช่หัวหน้ากลุ่มกระบวนการ เมื่อกลับกระบวนการเรียกจะเป็นผู้นำเซสชันของเซสชั่นใหม่นี้จะต้องเป็นหัวหน้ากลุ่มกระบวนการของกลุ่มกระบวนการใหม่และจะต้องไม่มีสถานีควบคุม รหัสกลุ่มกระบวนการของกระบวนการที่เรียกจะต้องตั้งค่าเท่ากับ ID กระบวนการของกระบวนการเรียก กระบวนการเรียกใช้จะเป็นกระบวนการเดียวในกลุ่มกระบวนการใหม่และกระบวนการเดียวในเซสชันใหม่
ซึ่งฉันหมายความว่าคุณสามารถสร้างกลุ่มจากกระบวนการเริ่มต้น ฉันใช้สิ่งนี้ใน php เพื่อที่จะสามารถฆ่าต้นไม้กระบวนการทั้งหมดหลังจากเริ่มต้นมัน
นี่อาจเป็นความคิดที่ไม่ดี ฉันจะสนใจในความคิดเห็น
แรงบันดาลใจจากความคิดเห็นของ ysth
kill -- -PGID
แทนที่จะให้มันเป็นหมายเลขกระบวนการให้มันปฏิเสธของหมายเลขกลุ่ม ตามปกติด้วยคำสั่งเกือบทุกชนิดหากคุณต้องการอาร์กิวเมนต์ปกติที่ขึ้นต้นด้วย a
-
ไม่สามารถตีความได้ว่าเป็นสวิตช์ให้นำหน้าด้วย--
ฟังก์ชั่นเชลล์ต่อไปนี้คล้ายกับคำตอบอื่น ๆ มากมาย แต่สามารถใช้งานได้ทั้งบน Linux และ BSD (OS X และอื่น ๆ ) โดยไม่ต้องพึ่งพาภายนอกเช่นpgrep
:
killtree() {
local parent=$1 child
for child in $(ps -o ppid= -o pid= | awk "\$1==$parent {print \$2}"); do
killtree $child
done
kill $parent
}
$child
ฟังก์ชั่นนั้นเพื่อที่จะไม่รบกวนตัวแปรอื่น ๆ (ที่ไม่ใช่ในท้องถิ่น) ด้วยชื่อเดียวกันและเพื่อให้แน่ใจว่าค่าของตัวแปรท้องถิ่นจะถูกล้างออกหลังจากฟังก์ชั่นสิ้นสุดลง
ตามคำตอบของ zhigang สิ่งนี้หลีกเลี่ยงการฆ่าตัวตาย:
init_killtree() {
local pid=$1 child
for child in $(pgrep -P $pid); do
init_killtree $child
done
[ $pid -ne $$ ] && kill -kill $pid
}
หากคุณต้องการฆ่ากระบวนการตามชื่อ:
killall -9 -g someprocessname
หรือ
pgrep someprocessname | xargs pkill -9 -g
นี่เป็นเวอร์ชั่นของฉันที่จะฆ่ากระบวนการลูกทั้งหมดโดยใช้ bash script มันไม่ได้ใช้การสอบถามซ้ำและขึ้นอยู่กับคำสั่ง pgrep
ใช้
killtree.sh PID SIGNAL
เนื้อหาของ killtrees.sh
#!/bin/bash
PID=$1
if [ -z $PID ];
then
echo "No pid specified"
fi
PPLIST=$PID
CHILD_LIST=`pgrep -P $PPLIST -d,`
while [ ! -z "$CHILD_LIST" ]
do
PPLIST="$PPLIST,$CHILD_LIST"
CHILD_LIST=`pgrep -P $CHILD_LIST -d,`
done
SIGNAL=$2
if [ -z $SIGNAL ]
then
SIGNAL="TERM"
fi
#do substring from comma to space
kill -$SIGNAL ${PPLIST//,/ }
นี่คือความแตกต่างของคำตอบของ @ zhigang ซึ่งทำโดยไม่มี AWK อาศัยความเป็นไปได้ในการแยกวิเคราะห์ของ Bash เท่านั้น:
function killtree {
kill -STOP "$1"
ps -e -o pid= -o ppid= | while read -r pid ppid
do
[[ $ppid = $1 ]] || continue
killtree "$pid" || true # Skip over failures
done
kill -CONT "$1"
kill -TERM "$1"
}
ดูเหมือนว่าจะทำงานได้ดีบนทั้ง Mac และ Linux ในสถานการณ์ที่คุณไม่สามารถพึ่งพาการจัดการกลุ่มกระบวนการเช่นเมื่อเขียนสคริปต์เพื่อทดสอบซอฟต์แวร์ที่ต้องสร้างในสภาพแวดล้อมที่หลากหลาย - เทคนิคการเดินแบบต้นไม้นี้มีประโยชน์อย่างแน่นอน
ขอบคุณสำหรับภูมิปัญญาของคุณ สคริปต์ของฉันออกจากกระบวนการเด็กบางส่วนเมื่อออกและเคล็ดลับการปฏิเสธทำให้ทุกอย่างง่ายขึ้น ฉันเขียนฟังก์ชันนี้เพื่อใช้ในสคริปต์อื่นหากจำเป็น:
# kill my group's subprocesses: killGroup
# kill also myself: killGroup -x
# kill another group's subprocesses: killGroup N
# kill that group all: killGroup -x N
# N: PID of the main process (= process group ID).
function killGroup () {
local prid mainpid
case $1 in
-x) [ -n "$2" ] && kill -9 -$2 || kill -9 -$$ ;;
"") mainpid=$$ ;;
*) mainpid=$1 ;;
esac
prid=$(ps ax -o pid,pgid | grep $mainpid)
prid=${prid//$mainpid/}
kill -9 $prid 2>/dev/null
return
}
ไชโย
หากคุณมี pstree และ perl ในระบบของคุณคุณสามารถลอง:
perl -e 'kill 9, (`pstree -p PID` =~ m/\((\d+)\)/sg)'
น่าจะเป็นการดีกว่าที่จะฆ่าพ่อแม่ก่อนลูก มิฉะนั้นผู้ปกครองอาจวางไข่เด็กใหม่อีกครั้งก่อนที่เขาจะฆ่าตัวตาย สิ่งเหล่านี้จะรอดชีวิตจากการฆ่า
รุ่น ps ของฉันแตกต่างจากข้างบน อาจจะแก่เกินไปดังนั้น grepping แปลก ...
วิธีใช้เชลล์สคริปต์แทนฟังก์ชันเชลล์มีข้อดีหลายประการ ...
อย่างไรก็ตามมันเป็นความคิดพื้นฐานของ zhigang
#!/bin/bash
if test $# -lt 1 ; then
echo >&2 "usage: kiltree pid (sig)"
fi ;
_pid=$1
_sig=${2:-TERM}
_children=$(ps j | grep "^[ ]*${_pid} " | cut -c 7-11) ;
echo >&2 kill -${_sig} ${_pid}
kill -${_sig} ${_pid}
for _child in ${_children}; do
killtree ${_child} ${_sig}
done
สิ่งต่อไปนี้ได้รับการทดสอบบน FreeBSD, Linux และ MacOS X และขึ้นอยู่กับ pgrep และ kill เท่านั้น (เวอร์ชัน ps -o ไม่ทำงานภายใต้ BSD) อาร์กิวเมนต์แรกคือ parent pid ที่ลูกต้องถูกยกเลิก อาร์กิวเมนต์ที่สองคือบูลีนเพื่อพิจารณาว่า pid พาเรนต์ต้องถูกยกเลิกเช่นกัน
KillChilds() {
local pid="${1}"
local self="${2:-false}"
if children="$(pgrep -P "$pid")"; then
for child in $children; do
KillChilds "$child" true
done
fi
if [ "$self" == true ]; then
kill -s SIGTERM "$pid" || (sleep 10 && kill -9 "$pid" &)
fi
}
KillChilds $$ > /dev/null 2>&1
สิ่งนี้จะส่ง SIGTERM ไปยังกระบวนการ child / grandchild ใด ๆ ภายในเชลล์สคริปต์และหาก SIGTERM ไม่สำเร็จมันจะรอ 10 วินาทีจากนั้นส่ง kill
คำตอบก่อนหน้านี้:
ต่อไปนี้ใช้งานได้ แต่จะฆ่าเชลล์ตัวเองใน BSD
KillSubTree() {
local parent="${1}"
for child in $(ps -o pid=$parent); do
if [ $$ -ne $child ]; then (kill -s SIGTERM $child || (sleep 10 && kill -9 $child & )) > /dev/null 2>&1 ; fi
done
}
# Example lanch from within script
KillSubTree $$ > /dev/null 2>&1
ฉันพัฒนาวิธีแก้ปัญหาของ zhigang, xyuri และ solidsneck เพิ่มเติม:
#!/bin/bash
if test $# -lt 1 ; then
echo >&2 "usage: kiltree pid (sig)"
exit 1 ;
fi ;
_pid=$1
_sig=${2:-TERM}
# echo >&2 "killtree($_pid) mypid = $$"
# ps axwwf | grep -6 "^[ ]*$_pid " >&2 ;
function _killtree () {
local _children
local _child
local _success
if test $1 -eq $2 ; then # this is killtree - don't commit suicide!
echo >&2 "killtree can´t kill it´s own branch - some processes will survive." ;
return 1 ;
fi ;
# this avoids that children are spawned or disappear.
kill -SIGSTOP $2 ;
_children=$(ps -o pid --no-headers --ppid $2) ;
_success=0
for _child in ${_children}; do
_killtree $1 ${_child} $3 ;
_success=$(($_success+$?)) ;
done ;
if test $_success -eq 0 ; then
kill -$3 $2
fi ;
# when a stopped process is killed, it will linger in the system until it is continued
kill -SIGCONT $2
test $_success -eq 0 ;
return $?
}
_killtree $$ $_pid $_sig
รุ่นนี้จะหลีกเลี่ยงการฆ่าบรรพบุรุษ - ซึ่งเป็นสาเหตุให้เกิดกระบวนการเด็กในการแก้ไขปัญหาก่อนหน้านี้
กระบวนการหยุดทำงานอย่างถูกต้องก่อนที่จะพิจารณารายการย่อยดังนั้นจึงไม่มีการสร้างลูกใหม่หรือหายไป
หลังจากถูกฆ่างานที่หยุดจะต้องหายไปจากระบบต่อไป
คำถามเก่าฉันรู้ แต่คำตอบทั้งหมดดูเหมือนจะเรียก ps ซึ่งฉันไม่ชอบ
โซลูชันที่ใช้ awk นี้ไม่ต้องการการเรียกซ้ำและเรียกใช้ ps เพียงครั้งเดียว
awk 'BEGIN {
p=1390
while ("ps -o ppid,pid"|getline) a[$1]=a[$1]" "$2
o=1
while (o==1) {
o=0
split(p, q, " ")
for (i in q) if (a[q[i]]!="") {
p=p""a[q[i]]
o=1
a[q[i]]=""
}
}
system("kill -TERM "p)
}'
หรือบนบรรทัดเดียว:
awk 'BEGIN {p=1390;while ("ps -o ppid,pid"|getline) a[$1]=a[$1]" "$2;o=1;while (o==1) {o=0;split(p, q, " ");for (i in q) {if (a[q[i]]!="") {p=p""a[q[i]];o=1;a[q[i]]=""}}}system("kill -TERM "p)}'
โดยพื้นฐานแล้วความคิดคือเราสร้างอาร์เรย์ (a) ของรายการ parent: รายการลูกจากนั้นวนรอบอาร์เรย์หาเด็ก ๆ สำหรับผู้ปกครองที่ตรงกันของเราเพิ่มเข้าไปในรายการผู้ปกครองของเรา (p)
หากคุณไม่ต้องการฆ่ากระบวนการระดับบนสุดให้ทำ
sub(/[0-9]*/, "", p)
ก่อนที่บรรทัด system () จะลบออกจากชุดคิท
โปรดจำไว้ว่ามีสภาพการแข่งขันที่นี่ แต่จริง (เท่าที่ฉันเห็น) ของการแก้ปัญหาทั้งหมด มันทำในสิ่งที่ฉันต้องการเพราะสคริปต์ที่ฉันต้องการไม่ใช่การสร้างเด็กอายุสั้นจำนวนมาก
แบบฝึกหัดสำหรับผู้อ่านจะทำให้เป็นวง 2 รอบ: หลังจากผ่านครั้งแรกส่ง SIGSTOP ไปยังกระบวนการทั้งหมดในรายการ p จากนั้นวนรอบเพื่อเรียกใช้ ps อีกครั้งและหลังจากผ่านครั้งที่สองส่ง SIGTERM จากนั้น SIGCONT หากคุณไม่สนเรื่องตอนจบที่ดีถ้างั้นรหัสผ่านที่สองก็น่าจะ SIGKILL ฉันคิดว่า
หากคุณรู้ว่า pid ของสิ่งที่คุณต้องการฆ่าคุณสามารถไปจาก session id และทุกอย่างใน session เดียวกัน ฉันจะตรวจสอบอีกครั้ง แต่ฉันใช้สิ่งนี้สำหรับสคริปต์ที่เริ่ม rsyncs ในลูปที่ฉันต้องการตายและไม่เริ่มต้นใหม่ (เนื่องจากการวนซ้ำ) ตามที่ต้องการหากฉันเพิ่งจะ rsall ฆ่าทั้งหมด
kill $(ps -o pid= -s $(ps -o sess --no-heading --pid 21709))
หากคุณไม่ทราบว่า pid คุณยังสามารถซ้อนได้มากขึ้น
kill $(ps -o pid= -s $(ps -o sess --no-heading --pid $(pgrep rsync )))
ps -o pid= --ppid $PPID | xargs kill -9
kill -9
จริงๆ
kill -15
จะไม่ช่วย
ใน sh คำสั่ง jobs จะแสดงรายการกระบวนการพื้นหลัง ในบางกรณีอาจเป็นการดีกว่าที่จะฆ่ากระบวนการใหม่ก่อนเช่นที่เก่ากว่าสร้างซ็อกเก็ตที่ใช้ร่วมกัน ในกรณีเหล่านี้เรียงลำดับ PID ในลำดับย้อนกลับ บางครั้งคุณต้องการรอสักครู่เพื่อให้งานเขียนบางอย่างลงบนดิสก์หรืออะไรแบบนั้นก่อนที่จะหยุด
และอย่าฆ่าถ้าคุณไม่ต้องทำ!
for SIGNAL in TERM KILL; do
for CHILD in $(jobs -s|sort -r); do
kill -s $SIGNAL $CHILD
sleep $MOMENT
done
done
ฆ่ากระบวนการลูกในเชลล์สคริปต์:
หลายครั้งที่เราต้องฆ่ากระบวนการเด็กที่ถูกแขวนคอหรือปิดกั้นด้วยเหตุผลบางอย่าง เช่น. ปัญหาการเชื่อมต่อ FTP
มีสองวิธีคือ
1) เพื่อสร้างผู้ปกครองใหม่แยกต่างหากสำหรับเด็กแต่ละคนซึ่งจะตรวจสอบและฆ่ากระบวนการเด็กเมื่อหมดเวลาถึง
สร้าง test.sh ดังนี้
#!/bin/bash
declare -a CMDs=("AAA" "BBB" "CCC" "DDD")
for CMD in ${CMDs[*]}; do
(sleep 10 & PID=$!; echo "Started $CMD => $PID"; sleep 5; echo "Killing $CMD => $PID"; kill $PID; echo "$CMD Completed.") &
done
exit;
และดูกระบวนการที่มีชื่อเป็น 'ทดสอบ' ในสถานีอื่นโดยใช้คำสั่งดังต่อไปนี้
watch -n1 'ps x -o "%p %r %c" | grep "test" '
สคริปต์ด้านบนจะสร้างกระบวนการลูกใหม่ 4 กระบวนการและผู้ปกครอง กระบวนการลูกแต่ละกระบวนการจะทำงานเป็นเวลา 10 วินาที แต่เมื่อหมดเวลาของการเข้าถึง 5 วินาทีกระบวนการพ่อแม่ของพวกเขาจะฆ่าเด็กเหล่านั้น ดังนั้นเด็กจะไม่สามารถดำเนินการให้เสร็จสมบูรณ์ได้ (10 วินาที) เล่นรอบเวลาเหล่านั้น (สลับ 10 และ 5) เพื่อดูพฤติกรรมอื่น ในกรณีที่เด็กจะเสร็จสิ้นการดำเนินการใน 5 วินาทีก่อนที่จะหมดเวลา 10 วินาที
2) ให้ผู้ปกครองปัจจุบันตรวจสอบและฆ่ากระบวนการเด็กเมื่อหมดเวลา สิ่งนี้จะไม่สร้างพาเรนต์แยกต่างหากเพื่อตรวจสอบเด็กแต่ละคน นอกจากนี้คุณสามารถจัดการกระบวนการลูกทั้งหมดอย่างถูกต้องภายในผู้ปกครองเดียวกัน
สร้าง test.sh ดังนี้
#!/bin/bash
declare -A CPIDs;
declare -a CMDs=("AAA" "BBB" "CCC" "DDD")
CMD_TIME=15;
for CMD in ${CMDs[*]}; do
(echo "Started..$CMD"; sleep $CMD_TIME; echo "$CMD Done";) &
CPIDs[$!]="$RN";
sleep 1;
done
GPID=$(ps -o pgid= $$);
CNT_TIME_OUT=10;
CNT=0;
while (true); do
declare -A TMP_CPIDs;
for PID in "${!CPIDs[@]}"; do
echo "Checking "${CPIDs[$PID]}"=>"$PID;
if ps -p $PID > /dev/null ; then
echo "-->"${CPIDs[$PID]}"=>"$PID" is running..";
TMP_CPIDs[$PID]=${CPIDs[$PID]};
else
echo "-->"${CPIDs[$PID]}"=>"$PID" is completed.";
fi
done
if [ ${#TMP_CPIDs[@]} == 0 ]; then
echo "All commands completed.";
break;
else
unset CPIDs;
declare -A CPIDs;
for PID in "${!TMP_CPIDs[@]}"; do
CPIDs[$PID]=${TMP_CPIDs[$PID]};
done
unset TMP_CPIDs;
if [ $CNT -gt $CNT_TIME_OUT ]; then
echo ${CPIDs[@]}"PIDs not reponding. Timeout reached $CNT sec. killing all childern with GPID $GPID..";
kill -- -$GPID;
fi
fi
CNT=$((CNT+1));
echo "waiting since $b secs..";
sleep 1;
done
exit;
และดูกระบวนการที่มีชื่อเป็น 'ทดสอบ' ในสถานีอื่นโดยใช้คำสั่งดังต่อไปนี้
watch -n1 'ps x -o "%p %r %c" | grep "test" '
สคริปต์ด้านบนจะสร้างกระบวนการลูกใหม่ 4 กระบวนการ เรากำลังจัดเก็บ pids ของกระบวนการลูกทั้งหมดและวนรอบพวกเขาเพื่อตรวจสอบว่าพวกเขาจะเสร็จสิ้นการดำเนินการของพวกเขาหรือยังคงทำงานอยู่ กระบวนการลูกจะดำเนินการจนถึงเวลา CMD_TIME แต่ถ้า CNT_TIME_OUT หมดเวลาเข้าถึงเด็กทุกคนจะถูกฆ่าโดยกระบวนการหลัก คุณสามารถสลับเวลาและเล่นกับสคริปต์เพื่อดูพฤติกรรม ข้อเสียเปรียบประการหนึ่งของวิธีนี้คือใช้รหัสประจำกลุ่มเพื่อฆ่าต้นไม้ลูกทั้งหมด แต่กระบวนการผู้ปกครองเองก็อยู่ในกลุ่มเดียวกันดังนั้นมันจึงถูกฆ่าเช่นกัน
คุณอาจต้องกำหนดรหัสกลุ่มอื่นให้กับกระบวนการผู้ปกครองหากคุณไม่ต้องการให้ผู้ปกครองถูกฆ่า
รายละเอียดเพิ่มเติมสามารถดูได้ที่นี่
สคริปต์นี้ยังใช้งานได้:
#/bin/sh
while true
do
echo "Enter parent process id [type quit for exit]"
read ppid
if [ $ppid -eq "quit" -o $ppid -eq "QUIT" ];then
exit 0
fi
for i in `ps -ef| awk '$3 == '$ppid' { print $2 }'`
do
echo killing $i
kill $i
done
done
ฉันรู้ว่ามันเก่า แต่นั่นเป็นทางออกที่ดีกว่าที่ฉันพบ:
killtree() {
for p in $(pstree -p $1 | grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*" | tac);do
echo Terminating: $p
kill $p
done
}