Thanh lịch có được danh sách các quá trình con cháu


23

Tôi muốn có một danh sách tất cả các quá trình đi xuống (ví dụ như trẻ em, cháu, v.v.) từ đó $pid. Đây là cách đơn giản nhất mà tôi nghĩ ra:

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

Có bất kỳ lệnh, hoặc bất kỳ cách đơn giản hơn để có được danh sách đầy đủ của tất cả các quá trình con cháu?


Có một lý do bạn cần tất cả chúng trên một dòng? Bạn đang làm gì với đầu ra đó? Tôi có cảm giác rằng đây là một vấn đề xy, và bạn đang hỏi sai câu hỏi.
jordanm

Tôi không quan tâm đến định dạng miễn là nó sạch (nghĩa là tôi không quan tâm đến việc '\n'phân định so với ' 'phân cách). Trường hợp sử dụng thực tế là: a) một tập lệnh trình nền mà tôi đã viết ra khỏi chủ nghĩa khổ dâm thuần túy (cụ thể, chức năng "dừng" phải xử lý bất kỳ cây quy trình nào mà quy trình được tạo ra); và b) một tập lệnh hết thời gian sẽ giết bất cứ quy trình hết thời gian nào được quản lý để tạo.
STenyaK

2
@STenyaK Các trường hợp sử dụng của bạn khiến tôi nghĩ rằng bạn đang tìm kiếm các nhóm quy trình và một đối số phủ định kill. Xem unix.stackexchange.com/questions/9480/... , unix.stackexchange.com/questions/50555/...
Gilles 'Somali dừng vốn là xấu'

@Gilles sử dụng ps ax -opid,ppid,pgrp,cmdTôi thấy có nhiều quy trình chia sẻ giống pgrpnhư cây con chính xác mà tôi muốn giết. (Ngoài ra, tôi không thể nhìn thấy setpgrpchương trình liệt kê bất cứ nơi nào trong gói debian ổn định: packages.debian.org/... )
STenyaK

1
Một trường hợp sử dụng khác: renice / ionice trên toàn bộ cây quy trình đang ăn quá nhiều tài nguyên, ví dụ: một bản dựng song song lớn.
Cheetah

Câu trả lời:


15

Cách sau đây có phần đơn giản hơn và có thêm lợi thế là bỏ qua các số trong tên lệnh:

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

Hoặc với Perl:

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

Chúng tôi đang tìm kiếm các số trong ngoặc đơn để chúng tôi không lấy ví dụ là 2 khi chúng tôi chạy qua gif2png(3012). Nhưng nếu tên lệnh chứa số được ngoặc đơn, tất cả các cược sẽ bị tắt. Chỉ có xử lý văn bản cho đến nay có thể đưa bạn.

Vì vậy, tôi cũng nghĩ rằng các nhóm quá trình là con đường để đi. Nếu bạn muốn có một quy trình chạy trong nhóm quy trình riêng của mình, bạn có thể sử dụng công cụ 'pgrphack' từ gói Debian 'daemontools':

pgrphack my_command args

Hoặc bạn có thể một lần nữa chuyển sang Perl:

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

Nhắc nhở duy nhất ở đây là các nhóm quy trình không lồng nhau, vì vậy nếu một số quy trình đang tạo các nhóm quy trình riêng, các quy trình con của nó sẽ không còn nằm trong nhóm mà bạn đã tạo.


Các quy trình con là tùy ý và có thể hoặc không thể sử dụng các nhóm quy trình (tôi không thể thừa nhận bất cứ điều gì). Tuy nhiên, câu trả lời của bạn gần nhất với những gì có thể đạt được trong Linux, vì vậy tôi sẽ chấp nhận nó. Cảm ơn.
STenyaK

Điều này rất hữu ích!
Michal Gallovic

Các ống pstree cũng sẽ bao gồm các id luồng, tức là ID của các luồng mà $ pid đã bắt đầu.
maxschlepzig

Bạn có thể sử dụng một grep duy nhất:pstree -lp | grep -Po "(?<=\()\d+(?=\))"
puchu

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

Nó sẽ là duy nhất đáng chú ý rằng điều này sẽ làm việc trên vỏ hiện đại ( bash, zsh, fish, và thậm chí cả ksh 99), nhưng có thể không làm việc trên vỏ cũ, ví dụ nhưksh 88
grochmal

@grochmal, xem câu trả lời của tôi dưới đây để biết giải pháp truyền tải hoạt động trong ksh-88.
maxschlepzig

1

Phiên bản ngắn nhất mà tôi đã tìm thấy cũng xử lý chính xác các lệnh như pop3d:

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

Nó xử lý sai nếu bạn có các lệnh có tên lạ như : my(23)prog.


1
Điều này không hoạt động đối với các lệnh đang chạy một số luồng (vì pstree cũng in các ID đó).
maxschlepzig

@maxschlepzig Nhận thấy rằng rất có vấn đề với ffmpegviệc sử dụng chủ đề. Mặc dù, từ những quan sát nhanh chóng, có vẻ như các chủ đề được đưa ra với tên của chúng bên trong dấu ngoặc nhọn , { }.
Spellweaver Gypsy

1

Ngoài ra còn có vấn đề về tính đúng đắn. Phân tích cú pháp đầu ra của pstreecó vấn đề vì một số lý do:

  • pstree hiển thị các mã PID id của các luồng (tên được hiển thị trong dấu ngoặc nhọn)
  • một tên lệnh có thể chứa dấu ngoặc nhọn, số trong ngoặc đơn khiến cho việc phân tích cú pháp đáng tin cậy là không thể

Nếu bạn đã psutilcài đặt Python và gói, bạn có thể sử dụng đoạn mã này để liệt kê tất cả các quy trình hậu duệ:

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

(Gói psutil, ví dụ như được cài đặt như một phần phụ thuộc của tracerlệnh có sẵn trên Fedora / CentOS.)

Ngoài ra, bạn có thể thực hiện một giao dịch theo chiều rộng đầu tiên của cây quy trình trong vỏ bourne:

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

Để tính toán sự đóng mở bắc cầu của pid, phần đuôi có thể được bỏ qua.

Lưu ý rằng ở trên không sử dụng đệ quy và cũng chạy trong ksh-88.

Trên Linux, người ta có thể loại bỏ pgrepcuộc gọi và thay vào đó đọc thông tin từ /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

Điều này hiệu quả hơn vì chúng tôi lưu một fork / exec cho mỗi PID và pgrepthực hiện một số công việc bổ sung trong mỗi cuộc gọi.


1

Phiên bản Linux này chỉ cần / Proc và ps. Nó được chuyển thể từ phần cuối của câu trả lời xuất sắc của @ maxschlepzig . Phiên bản này đọc / Proc trực tiếp từ shell thay vì sinh ra một quy trình phụ trong một vòng lặp. Nó nhanh hơn một chút và có thể thanh lịch hơn một chút, vì tiêu đề chủ đề này yêu cầu.

#!/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

Trong mỗi trường hợp sử dụng hai (dường như rất giả tạo) của bạn, tại sao bạn muốn giết một số quy trình phụ không may? Làm thế nào để bạn biết rõ hơn một quá trình khi con của nó nên sống hay chết? Điều này có vẻ như thiết kế kém với tôi; một quá trình nên làm sạch sau khi chính nó.

Nếu bạn thực sự hiểu rõ hơn, thì bạn nên bỏ qua các quy trình phụ này và 'quy trình được quy định' rõ ràng là quá ngu ngốc để được tin tưởng fork(2) .

Bạn nên tránh giữ danh sách các quy trình con hoặc dò dẫm qua cây quy trình, ví dụ: bằng cách đặt các quy trình con vào một nhóm quy trình riêng biệt theo đề xuất của @Gilles.

Trong mọi trường hợp, tôi nghi ngờ rằng quy trình được tạo ra của bạn sẽ tốt hơn là tạo ra một nhóm luồng công nhân (mà nhất thiết phải chết cùng với quy trình chứa của nó) hơn là một cây sâu của các quy trình phụ, mà một cái gì đó sau đó phải dọn sạch .


2
Cả hai trường hợp sử dụng đều được sử dụng trong môi trường tích hợp / kiểm tra liên tục, vì vậy chúng phải xử lý khả năng xảy ra lỗi trong quy trình con. Lỗi này có thể biểu hiện là không có khả năng tự tắt máy hoặc con cái của họ, vì vậy tôi cần một cách để đảm bảo rằng tôi có thể đóng tất cả chúng trong trường hợp xấu nhất.
STenyaK

1
Trong trường hợp đó, tôi với @Gilles và @Jander; nhóm quá trình là cách tốt nhất.
AnotherSmellyGeek

0

Đây là tập lệnh bao bọc pgrep cho phép bạn sử dụng pgrep và nhận tất cả các hậu duệ cùng một lúc.

~/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

Gọi giống như cách bạn gọi pgrep bình thường, chẳng hạn như pgrep_recursive -U $USER javađể tìm tất cả các quy trình Java và quy trình phụ từ người dùng hiện tại.


1
Vì đây là bash, tôi có cảm giác mã được sử dụng để tham gia các PID với dấu phân cách có thể được thay thế bằng cài đặt IFSvà sử dụng mảng ( "${array[*]}").
muru
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.