Làm thế nào để có được ID quá trình của quá trình nền?


375

Tôi bắt đầu một quá trình nền từ kịch bản shell của mình và tôi muốn hủy quá trình này khi kịch bản của tôi kết thúc.

Làm thế nào để có được PID của quá trình này từ tập lệnh shell của tôi? Theo như tôi có thể thấy biến $!chứa PID của tập lệnh hiện tại, không phải là quá trình nền.


8
$! đúng. Bạn có chắc là bạn đang bắt đầu tập lệnh trong BG không? xin vui lòng mẫu.
pixelbeat

7
Có $! đúng. Tôi đã sai.
Volodymyr Bezuglyy

11
$$ chứa tập lệnh hiện tại PID.
HUB

Câu trả lời:


583

Bạn cần lưu PID của quá trình nền tại thời điểm bạn khởi động nó:

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

Bạn không thể sử dụng kiểm soát công việc, vì đó là một tính năng tương tác và được gắn với một thiết bị đầu cuối kiểm soát. Một tập lệnh sẽ không nhất thiết phải có một thiết bị đầu cuối kèm theo vì vậy kiểm soát công việc sẽ không nhất thiết phải có sẵn.


22
Kể từ $! trả về pid của quá trình nền cuối cùng. Có thể là một cái gì đó bắt đầu giữa foo$!, và chúng ta nhận được rằng một cái gì đó thay vì foo?
WiSaGaN

53
@WiSaGaN: Không. Không có gì giữa những dòng đó. Bất kỳ hoạt động nào khác trên hệ thống sẽ không ảnh hưởng đến điều này. $! sẽ mở rộng tới PID của quá trình nền cuối cùng trong shell đó .
camh

8
... mà vòi bạn nếu fooxảy ra là nhiều lệnh đường ống (ví dụ. tail -f somefile.txt | grep sometext). Trong những trường hợp như vậy, bạn sẽ nhận được lệnh PID của lệnh grep $!chứ không phải lệnh đuôi nếu đó là những gì bạn đang tìm kiếm. Bạn sẽ cần phải sử dụng jobshoặc pshoặc thích trong trường hợp này.
John Rix

1
@JohnRix: Không nhất thiết. Bạn sẽ nhận được thông báo grep, nhưng nếu bạn giết nó, đuôi sẽ nhận được SIGPIPE khi nó cố gắng ghi vào đường ống. Nhưng ngay khi bạn cố gắng vào bất kỳ quy trình quản lý / kiểm soát khó khăn nào, bash / shell trở nên khá đau đớn.
camh

5
Một giải pháp xứng đáng khác được đề xuất trong (một bình luận cho một câu trả lời) Làm thế nào để có được thông tin về quá trình mới bắt đầu : oh, và "oneliner": /bin/sh -c 'echo $$>/tmp/my.pid && exec program args' &- sysfault 24 tháng 11 '10 lúc 14:28
imz - Ivan Zakharyaschev

148

Bạn có thể sử dụng jobs -llệnh để đến một công việc cụ thể

^Z
[1]+  Stopped                 guard

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

Trong trường hợp này, 46841 là PID.

Từ help jobs:

-l Báo cáo ID nhóm quy trình và thư mục làm việc của các công việc.

jobs -p là một tùy chọn khác chỉ hiển thị các PID.


Để sử dụng điều này trong tập lệnh shell, bạn phải xử lý đầu ra.
Phil

6
@Phil Để chỉ liệt kê các pids: jobs -p. Để liệt kê pid của một công việc nhất định: công việc -p% 3. Không cần xử lý đầu ra.
Erik Aronesty

1
@Erik với các lệnh / đối số khác nhau, bạn thay đổi bối cảnh nhận xét của tôi. Không có đối số bổ sung đề xuất đầu ra yêu cầu xử lý. Đề nghị cải tiến cho câu trả lời!
Phil

Lưu PID từ $!ngay sau khi bạn khởi động, nó dễ mang theo hơn và đơn giản hơn trong hầu hết các tình huống. Đó là những gì câu trả lời hiện đang được chấp nhận.
tripleee

jobs -pđã trả lại giống như jobs -ltrên LubFi 16.4
Timo

46
  • $$ là kịch bản hiện tại
  • $! là mấu chốt của quá trình nền cuối cùng

Đây là bản ghi mẫu từ phiên bash ( %1đề cập đến số thứ tự của quy trình nền như được thấy từ jobs):

$ echo $$
3748

$ sleep 100 &
[1] 192

$ echo $!
192

$ kill %1

[1]+  Terminated              sleep 100

echo %1không trả về quá trình nền trên Ubuntu của tôi trong khi echo $!đó
Timo

25

Một cách thậm chí đơn giản hơn để giết tất cả tiến trình con của tập lệnh bash:

pkill -P $$

Các -Plá cờ hoạt động theo cách tương tự với pkillpgrep- nó được tiến trình con, chỉ với pkillcác quá trình đứa trẻ bị chết và với pgrepPID con được in để stdout.


Rât thuận tiện! Đây là cách tốt nhất để đảm bảo bạn không để các quy trình mở trên nền.
Lepe

@lepe: Không hẳn. Nếu bạn là ông bà, điều này sẽ không hiệu quả: sau khi bash -c 'bash -c "sleep 300 &"' & chạy pgrep -P $$không có gì, vì giấc ngủ sẽ không phải là đứa con trực tiếp của bạn.
petre

1
@AlexeyPolonsky: nên là: giết tất cả các procs của shell, không phải là script. Bởi vì $$đề cập đến vỏ hiện tại.
Timo

biểu diễn bash -c 'bash -c "sleep 300 &"' & ; pgrep -P $$, tôi nhận được trên [1] <pid>. So at least it shows something, but this is probably not the output of stgout pgrep`
Timo

Ngay cả các quy trình cũng có thể kết nối chúng từ quy trình cha. Thủ thuật đơn giản là gọi nĩa (tạo cháu) và sau đó chỉ cần để con xử lý thoát trong khi cháu vẫn tiếp tục làm việc. (daemonization là từ khóa) Nhưng ngay cả khi trẻ tiếp tục chạy quá pkill -P không đủ để truyền tín hiệu đến cháu. Công cụ như pstree được yêu cầu để theo toàn bộ cây quy trình phụ thuộc. Nhưng điều này sẽ không bắt được daemon bắt đầu từ quá trình vì cha mẹ của chúng là quá trình 1. ví dụ:bash -c 'bash -c "sleep 10 & wait $!"' & sleep 0.1; pstree -p $$
Pauli Nieminen

4

đây là những gì tôi đã làm Kiểm tra nó, hy vọng nó có thể giúp đỡ.

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

Sau đó, chỉ cần chạy nó như: ./bgkill.shvới quyền thích hợp tất nhiên

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

Bạn cũng có thể sử dụng pstree:

pstree -p user

Điều này thường đưa ra biểu diễn văn bản của tất cả các quy trình cho "người dùng" và tùy chọn -p cung cấp cho id quy trình. Theo như tôi hiểu thì không phụ thuộc vào việc các quy trình được sở hữu bởi trình bao hiện tại. Nó cũng cho thấy dĩa.


1
Để sử dụng điều này trong tập lệnh shell, bạn phải xử lý rất nhiều đầu ra.
Phil

1

pgrepcó thể giúp bạn có tất cả các PID con của một tiến trình cha. Như đã đề cập trước đó $$là các kịch bản hiện tại PID. Vì vậy, nếu bạn muốn một tập lệnh tự dọn sạch, thì nên thực hiện thủ thuật này:

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

Điều này sẽ không giết nhiều?
Phil

Vâng, câu hỏi không bao giờ đề cập đến việc giữ một số trẻ em sống sót khi thoát ra.
errant.info

trap 'pkill -P $$' SIGING SIGTERM EXITTrông đơn giản hơn, nhưng tôi đã không kiểm tra nó.
petre

Để tương thích, không sử dụng SIGtiền tố. Nó được POSIX cho phép nhưng cũng giống như một tiện ích mở rộng mà việc triển khai có thể hỗ trợ: pubs.opengroup.org/onlinepub/007904975/utilities/trap.html Ví dụ, vỏ dash không có.
josch
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.