รับรายการกระบวนการที่สืบทอดมาอย่างสง่างาม


23

ฉันต้องการที่จะได้รับรายชื่อของกระบวนการทั้งหมดที่ลงมา (เช่นเด็ก, แกรนด์เด็ก, ฯลฯ ) $pidจาก นี่เป็นวิธีที่ง่ายที่สุดที่ฉันเคยทำ:

pstree -p $pid | tr "\n" " " |sed "s/[^0-9]/ /g" |sed "s/\s\s*/ /g"

มีคำสั่งหรือวิธีที่ง่ายกว่าในการรับรายการทั้งหมดของกระบวนการสืบทอดทั้งหมดหรือไม่?


มีเหตุผลที่คุณต้องการพวกเขาทั้งหมดในหนึ่งบรรทัด? คุณกำลังทำอะไรกับเอาต์พุตนั้น ฉันรู้สึกว่านี่เป็นปัญหา xy และคุณกำลังถามคำถามผิด
jordanm

ฉันไม่สนใจรูปแบบตราบใดที่มันสะอาด (เช่นฉันไม่สนใจเกี่ยวกับ'\n'ตัวคั่นและ' 'ตัวคั่น) กรณีใช้งานจริง: a)สคริปต์ daemonizer ที่ฉันเขียนออกมาจากลัทธิโซคิสต์บริสุทธิ์ (โดยเฉพาะฟังก์ชั่น "หยุด" จะต้องจัดการกับต้นไม้ใด ๆ ของกระบวนการที่กระบวนการ daemonized วางไข่); และb)สคริปต์หมดเวลาที่จะฆ่าทุกกระบวนการที่หมดเวลาที่จัดการเพื่อสร้าง
STenyaK

2
@STenyaK killกรณีการใช้งานของคุณทำให้ฉันคิดว่าคุณกำลังมองหากลุ่มกระบวนการและการโต้แย้งในทางลบต่อ ดูunix.stackexchange.com/questions/9480/… , unix.stackexchange.com/questions/50555/…
Gilles 'SO - หยุดความชั่วร้าย'

@Gilles ใช้ps ax -opid,ppid,pgrp,cmdฉันเห็นว่ามีหลายกระบวนการที่ใช้ร่วมกับpgrpทรีย่อยที่แน่นอนที่ฉันต้องการฆ่า (นอกจากนี้ฉันไม่สามารถดูsetpgrpรายการที่ใดก็ได้ในแพ็คเกจเสถียร debian: packages.debian.org/ … )
STenyaK

1
อีกกรณีการใช้: renice / ionice บนต้นไม้กระบวนการทั้งหมดที่กินทรัพยากรมากเกินไปเช่นการสร้างขนานขนาดใหญ่
เสือชีต้า

คำตอบ:


15

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

pstree -p $pid | grep -o '([0-9]\+)' | grep -o '[0-9]\+'

หรือด้วย Perl:

pstree -p $pid | perl -ne 'print "$1\n" while /\((\d+)\)/g'

เรากำลังมองหาตัวเลขในวงเล็บเพื่อให้เราไม่ได้ยกตัวอย่างเช่นให้ 2 gif2png(3012)เป็นกระบวนการเด็กเมื่อเราวิ่งข้าม แต่ถ้าชื่อคำสั่งมีหมายเลขวงเล็บการเดิมพันทั้งหมดจะปิด มีเพียงการประมวลผลข้อความที่สามารถนำคุณไปได้

ดังนั้นฉันจึงคิดว่ากลุ่มกระบวนการเป็นวิธีที่จะไป หากคุณต้องการให้กระบวนการทำงานในกลุ่มกระบวนการของตนเองคุณสามารถใช้เครื่องมือ 'pgrphack' จากแพคเกจ Debian 'daemontools':

pgrphack my_command args

หรือคุณสามารถหันไปใช้ Perl อีกครั้ง:

perl -e 'setpgid or die; exec { $ARGV[0] } @ARGV;' my_command args

ข้อแม้เท่านั้นที่นี่คือกลุ่มกระบวนการไม่ซ้อนกันดังนั้นหากบางกระบวนการกำลังสร้างกลุ่มกระบวนการของตัวเองกระบวนการย่อยจะไม่อยู่ในกลุ่มที่คุณสร้างอีกต่อไป


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

มันมีประโยชน์มาก!
Michal Gallovic

ท่อ pstree จะรวมรหัสเธรดเช่น ID ของเธรดที่เริ่มต้นด้วย $ pid
maxschlepzig

คุณสามารถใช้ grep เดี่ยว:pstree -lp | grep -Po "(?<=\()\d+(?=\))"
puchu

7
descendent_pids() {
    pids=$(pgrep -P $1)
    echo $pids
    for pid in $pids; do
        descendent_pids $pid
    done
}

มันจะเป็นเพียงที่น่าสังเกตว่านี้จะทำงานบนเปลือกหอยที่ทันสมัย ( bash, zsh, fishและแม้กระทั่งksh 99) แต่อาจจะไม่ทำงานในเปลือกหอยเก่าเช่นksh 88
grochmal

@ grochmal ดูคำตอบของฉันด้านล่างสำหรับวิธีการแก้ปัญหาการสำรวจเส้นทางที่ทำงานใน ksh-88
maxschlepzig

1

รุ่นที่สั้นที่สุดที่ฉันได้พบที่เกี่ยวข้องกับคำสั่งเช่นpop3d:

pstree -p $pid | perl -ne 's/\((\d+)\)/print " $1"/ge'

มันเกี่ยวข้องอย่างผิดพลาดถ้าคุณมีคำสั่งที่มีชื่อแปลก ๆ my(23)progเช่น:


1
สิ่งนี้ไม่ทำงานสำหรับคำสั่งที่ใช้งานเธรดบางตัว (เนื่องจาก pstree พิมพ์ ID เหล่านั้นด้วย)
maxschlepzig

@maxschlepzig พบว่ามีปัญหามากในการffmpegใช้เธรด ถึงแม้ว่าจากการสังเกตอย่างรวดเร็วดูเหมือนว่าจะมีเธรดพร้อมชื่อภายในวงเล็บปีกกา, { }.
Spellweaver ยิปซี

1

นอกจากนี้ยังมีปัญหาของความถูกต้อง การแยกวิเคราะห์ผลลัพธ์ของpstreeปัญหาอย่างไร้เหตุผลด้วยเหตุผลหลายประการ:

  • pstree แสดง PIDs และรหัสของกระทู้ (แสดงในเครื่องหมายปีกกา)
  • ชื่อคำสั่งอาจมีเครื่องหมายปีกกาหมายเลขในวงเล็บที่ทำให้การแยกวิเคราะห์ไม่น่าเชื่อถือ

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

pid=2235; python3 -c "import psutil
for c in psutil.Process($pid).children(True):
  print(c.pid)"

(แพ็คเกจ psutil นั้นได้รับการติดตั้งเช่นเดียวกับการพึ่งพาtracerคำสั่งที่มีอยู่ใน Fedora / CentOS)

อีกทางหนึ่งคุณสามารถทำการสำรวจเส้นทางที่กว้างเป็นอันดับแรกของแผนผังกระบวนการในเชลล์เบิร์น:

ps=2235; while [ "$ps" ]; do echo $ps; ps=$(echo $ps | xargs -n1 pgrep -P); \
  done | tail -n +2 | tr " " "\n"

สำหรับการคำนวณการปิด - ปิดของ pid สามารถตัดส่วนหางได้

โปรดทราบว่าข้างต้นไม่ได้ใช้การเรียกซ้ำและทำงานใน ksh-88

บน Linux ผู้ใช้หนึ่งสามารถกำจัดการpgrepโทรและอ่านข้อมูลจาก/proc:

ps=2235; while [ "$ps" ]; do echo $ps ; \
  ps=$(for p in $ps; do cat /proc/$p/task/$p/children; done); done \
  | tr " " "\n"' | tail -n +2

สิ่งนี้มีประสิทธิภาพมากขึ้นเพราะเราบันทึกหนึ่ง fork / exec สำหรับ PID แต่ละอันและpgrepทำงานเพิ่มเติมบางอย่างในการโทรแต่ละครั้ง


1

รุ่น Linux นี้ต้องการ / proc และ ps เท่านั้น มันดัดแปลงมาจากคำตอบที่ดีที่สุดของ @ maxschlepzig ชิ้นสุดท้าย รุ่นนี้อ่าน / proc โดยตรงจากเปลือกแทนที่จะวางไข่กระบวนการย่อยในวง มันเร็วขึ้นเล็กน้อยและมีเนื้อหาที่สวยงามกว่าเล็กน้อยเนื่องจากคำขอชื่อเธรดนี้

#!/bin/dash

# Print all descendant pids of process pid $1
# adapted from /unix//a/339071

ps=${1:-1}
while [ "$ps" ]; do
  echo $ps
  unset ps1 ps2
  for p in $ps; do
    read ps2 < /proc/$p/task/$p/children 2>/dev/null
    ps1="$ps1 $ps2"
  done
  ps=$ps1
done | tr " " "\n" | tail -n +2

0

ในแต่ละกรณีที่คุณใช้ (ดูเหมือนว่าเทียมมาก) ทำไมคุณถึงอยากจะฆ่ากระบวนการย่อยของกระบวนการที่โชคร้าย? คุณจะรู้ได้อย่างไรดีกว่ากระบวนการที่ลูกควรจะมีชีวิตอยู่หรือตาย? ดูเหมือนว่าการออกแบบที่ไม่ดีสำหรับฉัน กระบวนการควรล้างข้อมูลหลังจากนั้น

หากคุณรู้ดีกว่าจริง ๆ คุณควรฟอร์กกระบวนการย่อยเหล่านี้และ 'กระบวนการภูตผีปีศาจ' ดูเหมือนจะโง่เกินไปที่จะเชื่อถือได้ fork(2)เห็นได้ชัดคือโง่เกินไปที่จะไว้วางใจให้

คุณควรหลีกเลี่ยงการเก็บรายการของกระบวนการลูกหรือการวนผ่านกระบวนการต้นไม้เช่นโดยการวางกระบวนการเด็กในกลุ่มกระบวนการแยกตามที่แนะนำโดย @Gilles

ในกรณีใด ๆ ฉันสงสัยว่ากระบวนการ daemonized ของคุณจะดีกว่าการสร้างกลุ่มเธรดของผู้ปฏิบัติงาน (ซึ่งจำเป็นต้องตายพร้อมกับกระบวนการที่มี) กว่าต้นไม้ย่อยของกระบวนการย่อยย่อยซึ่งบางสิ่งบางอย่างต้องทำความสะอาด .


2
ทั้งสองกรณีใช้จะใช้ในสภาพแวดล้อมการรวม / ทดสอบอย่างต่อเนื่องดังนั้นพวกเขาจึงต้องจัดการกับความเป็นไปได้ของข้อผิดพลาดที่มีอยู่ในกระบวนการลูก / ES ข้อผิดพลาดนี้อาจแสดงให้เห็นว่าตัวเองไม่สามารถปิดตัวเองหรือลูก ๆ ของพวกเขาได้อย่างเหมาะสมดังนั้นฉันจึงต้องการวิธีที่จะทำให้แน่ใจว่าฉันสามารถปิดพวกเขาทั้งหมดในกรณีที่เลวร้ายที่สุด
STenyaK

1
ในกรณีนั้นฉันอยู่กับ @Gilles และ @Jander กลุ่มกระบวนการเป็นวิธีที่ดีที่สุด
AnotherSmellyGeek

0

ต่อไปนี้เป็นสคริปต์ pgrep wrapper ซึ่งให้คุณใช้ pgrep และรับลูกหลานทั้งหมดในเวลาเดียวกัน

~/bin/pgrep_wrapper:

#!/bin/bash

# the delimiter argument must be the first arg, otherwise it is ignored
delim=$'\n'
if [ "$1" == "-d" ]; then
    delim=$2
    shift 2
fi

pids=
newpids=$(pgrep "$@")
status=$?
if [ $status -ne 0 ]; then
    exit $status
fi

while [ "$pids" != "$newpids" ]; do
    pids=$newpids
    newpids=$( { echo "$pids"; pgrep -P "$(echo -n "$pids" | tr -cs '[:digit:]' ',')"; } | sort -u )
done
if [ "$delim" != $'\n' ]; then
    first=1
    for pid in $pids; do
        if [ $first -ne 1 ]; then
            echo -n "$delim"
        else
            first=0
        fi  
        echo -n "$pid"
    done
else
    echo "$pids"
fi

เรียกใช้วิธีเดียวกันกับที่คุณเรียกใช้ pgrep ปกติเช่นpgrep_recursive -U $USER javaเพื่อค้นหากระบวนการ Java และกระบวนการย่อยทั้งหมดจากผู้ใช้ปัจจุบัน


1
ตั้งแต่นี้เป็นทุบตีฉันมีความรู้สึกรหัสที่ใช้สำหรับการเข้าร่วม PIDs ที่มีตัวคั่นจะถูกแทนที่ด้วยการตั้งค่าIFSและการใช้อาร์เรย์ ( "${array[*]}").
Muru
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.