Làm thế nào để có được pid của quá trình vừa bắt đầu


71

Tôi muốn bắt đầu quá trình (ví dụ: myCommand) và lấy pid của nó (để cho phép giết nó sau).

Tôi đã thử ps và lọc theo tên, nhưng tôi không thể phân biệt quá trình theo tên

myCommand
ps ux | awk '/<myCommand>/ {print $2}' 

Bởi vì tên quy trình không phải là duy nhất.

Tôi có thể chạy tiến trình bằng cách:

myCommand &

Tôi thấy rằng tôi có thể có được PID này bằng cách:

echo $!

Có giải pháp nào đơn giản hơn không?

Tôi sẽ rất vui khi thực hiện myCommand và nhận được PID của nó như là kết quả của một dòng lệnh.

Câu trả lời:


77

Điều gì có thể đơn giản hơn echo $!? Như một dòng:

myCommand & echo $!

Cảm ơn bạn đã hợp nhất các lệnh này với "&" đã giúp tôi rất nhiều.
rafalmag

8
trong một tập lệnh bash, trong một vòng lặp khởi động các chương trình, $! không chính xác. Đôi khi, nó trả về pid của chính script, đôi khi grep hoặc awk chạy từ script bất kỳ giải pháp nào để có được pid của tiến trình vừa được đưa ra trong kịch bản này? một cái gì đó như pid = myprogramsẽ là tuyệt vời

2
Lưu ý rằng điều này yêu cầu bạn bắt đầu lệnh bằng cách sử dụng một &dòng như trước đó, nếu không, tiếng vang về cơ bản trả về trống.
rogerdpack 4/2/2015

2
gán cho biến như command & echo $!đóng băng việc thực thi ở bước này :(
Shashank Vivek


20

Bạn có thể sử dụng sh -cexecđể có được lệnh PID ngay cả trước khi nó chạy.

Để bắt đầu myCommand, để PID của nó được in trước khi bắt đầu chạy, bạn có thể sử dụng:

sh -c 'echo $$; exec myCommand'

Làm thế nào nó hoạt động:

Điều này bắt đầu một lớp vỏ mới, in ra bộ vỏ của lớp vỏ đó và sau đó sử dụng hàm execdựng sẵn để thay thế lớp vỏ bằng lệnh của bạn, đảm bảo nó có cùng bộ mã hóa. Khi shell của bạn chạy một lệnh với execnội dung, shell của bạn thực sự trở thành lệnh đó , chứ không phải là hành vi phổ biến hơn để lấy một bản sao mới của chính nó, có PID riêng và sau đó trở thành lệnh.

Tôi thấy điều này đơn giản hơn nhiều so với các lựa chọn thay thế liên quan đến thực thi không đồng bộ (với &), kiểm soát công việc hoặc tìm kiếm với ps. Các cách tiếp cận này đều ổn, nhưng trừ khi bạn có lý do cụ thể để sử dụng chúng - ví dụ, có lẽ lệnh đang chạy, trong trường hợp đó, việc tìm kiếm PID của nó hoặc sử dụng điều khiển công việc sẽ có ý nghĩa - tôi khuyên bạn nên xem xét cách này trước tiên. (Và tôi chắc chắn sẽ không xem xét việc viết một kịch bản phức tạp hoặc chương trình khác để đạt được điều này).

Câu trả lời này bao gồm một ví dụ về kỹ thuật này.


Các phần của lệnh đó đôi khi có thể được bỏ qua, nhưng không thường xuyên.

Ngay cả khi hệ vỏ bạn đang sử dụng là kiểu Bourne và do đó hỗ trợ execdựng sẵn với các ngữ nghĩa này, bạn thường không nên cố gắng sử dụng sh -c(hoặc tương đương) để tạo quy trình vỏ mới, riêng biệt cho mục đích này, bởi vì:

  • Khi shell đã trở thành myCommand, không có shell nào chờ chạy các lệnh tiếp theo. sh -c 'echo $$; exec myCommand; foosẽ không thể cố gắng chạy foosau khi thay thế bằng myCommand. Trừ khi bạn đang viết một tập lệnh chạy lệnh này như là lệnh cuối cùng của nó, bạn không thể chỉ sử dụng echo $$; exec myCommandtrong trình bao nơi bạn đang chạy các lệnh khác.
  • Bạn không thể sử dụng một subshell cho việc này. (echo $$; exec myCommand)về mặt cú pháp có thể đẹp hơn về mặt cú pháp sh -c 'echo $$; exec myCommand', nhưng khi bạn chạy $$vào bên trong ( ), nó sẽ cung cấp cho bộ vỏ cha mẹ chứ không phải của lớp vỏ con. Nhưng chính bộ vi xử lý của lớp con sẽ là bộ vi xử lý của lệnh mới. Một số shell cung cấp các cơ chế không di động của riêng chúng để tìm PID của lớp con, mà bạn có thể sử dụng cho việc này. Đặc biệt, trong Bash 4 , (echo $BASHPID; exec myCommand)không hoạt động.

Cuối cùng, lưu ý rằng một số shell sẽ thực hiện tối ưu hóa khi chúng chạy lệnh như thể exec(ví dụ, chúng sẽ bỏ qua trước) khi biết rằng shell sẽ không cần phải làm gì sau đó. Một số shell cố gắng thực hiện điều này bất cứ khi nào nó là lệnh cuối cùng được chạy, trong khi các shell khác sẽ chỉ thực hiện khi không có lệnh nào khác trước hoặc sau lệnh và những lệnh khác hoàn toàn không thực hiện. Hiệu quả là nếu bạn quên viết execvà chỉ sử dụng sh -c 'echo $$; myCommand'thì đôi khi nó sẽ cung cấp cho bạn đúng PID trên một số hệ thống có một số shell. Tôi khuyên bạn không nên dựa vào hành vi như vậy , và thay vào đó luôn luôn bao gồm cả execkhi đó là những gì bạn cần.


Trước khi tôi có thể chạy myCommand, tôi cần đặt một số biến môi trường trong tập lệnh bash của mình. Những thứ này sẽ chuyển sang môi trường mà execlệnh đang chạy?
dùng5359531

Có vẻ như môi trường của tôi thực hiện theo execlệnh. Tuy nhiên, phương pháp này không hoạt động khi myCommandbắt đầu các quy trình khác, đó là những quy trình bạn cần làm việc; Khi tôi phát ra một kill -INT <pid>nơi pidđược lấy theo cách này, tín hiệu không đến được các quy trình phụ được bắt đầu bởi myCommand, trong khi nếu tôi chạy myCommand trong phiên hiện tại và Ctrl + C, tín hiệu sẽ truyền đúng.
dùng5359531

1
Tôi đã thử điều này, nhưng pid của quá trình myCommand dường như là đầu ra pid bởi echo $$ +1. Tôi có làm điều gì sai?
crobar

Lệnh của tôi trông như thế này:sh -c 'echo $$; exec /usr/local/bin/mbdyn -f "input.file" -o "/path/to/outputdir" > "command_output.txt" 2>&1 &'
crobar

7

Tôi không biết về bất kỳ giải pháp đơn giản nào, nhưng không sử dụng $! đủ tốt? Bạn luôn có thể gán giá trị cho một số biến khác nếu bạn cần nó sau này, như những người khác nói.

Là một lưu ý phụ, thay vì đường ống từ ps bạn có thể sử dụng pgrephoặc pidof.


5

sử dụng exec từ tập lệnh bash sau khi đăng ký pid vào tập tin:

thí dụ:

giả sử bạn có một tập lệnh có tên là "mãi mãi.sh" mà bạn muốn chạy với args p1, p2, p3

mãi mãi.sh sourcecode:

#!/bin/sh

while [ 1 -lt 2 ] ; do
    logger "$0 running with parameters \"$@\""
    sleep 5
done

tạo một máy gặt.sh:

#!/bin/sh

echo $$ > /var/run/$1.pid
exec "$@"

chạy mãi mãi.sh qua reaper.sh:

./reaper.sh ./forever.sh p1 p2 p3 p4 &

mãi mãi.sh không làm gì khác hơn là đăng nhập một dòng để syslog mỗi 5 giây

bây giờ bạn có pid trong /var/run/forver.sh.pid

cat /var/run/forever.sh.pid 
5780

và mãi mãi.sh đang chạy aok. syslog grep:

Nov 24 16:07:17 pinkpony cia: ./forever.sh running with parameters "p1 p2 p3 p4"

bạn có thể thấy nó trong bảng quy trình:

ps axuwww|grep 'forever.sh p1' |grep -v grep
root      5780  0.0  0.0   4148   624 pts/7    S    16:07   0:00 /bin/sh ./forever.sh p1 p2 p3 p4

3
oh, và "oneliner": / bin / sh -c 'echo $$> / tmp / my.pid && exec chương trình args' &
user237419

1
Để bảo toàn đúng khoảng trắng bên trong các đối số, bạn nên sử dụng exec "$@"thay vì exec $*. Về mặt kỹ thuật, những gì bạn cần bảo tồn không phải là khoảng trắng mà là sự xuất hiện của các ký tự trong tham số shell IFS (mặc định là khoảng trắng, tab và dòng mới).
Chris Johnsen

điểm lấy. :)
user237419

Cảm ơn bạn, tôi không biết tham số $$. Nó có thể rất hữu ích.
rafalmag

3

Trong bash shell, một giải pháp thay thế $!có thể là tích jobs -phợp sẵn. Trong một số trường hợp, phần !in $!được giải thích bởi shell trước (hoặc thay vì) mở rộng biến, dẫn đến kết quả không mong muốn.

Điều này, ví dụ, sẽ không hoạt động:

((yourcommand) & echo $! >/var/run/pidfile)

trong khi điều này sẽ:

((yourcommand) & jobs -p >/var/run/pidfile)

Tôi nghĩ bạn có nghĩa là ((yourcommand) & jobs -p> / var / run / pidfile).
Dom

1

Bạn có thể sử dụng một cái gì đó như:

$ myCommand ; pid=$!

Hoặc là

$ myCommand && pid=$!

Hai lệnh có thể là khớp sử dụng ;hoặc &&. Trong trường hợp thứ hai, pid sẽ chỉ được đặt nếu lệnh đầu tiên thành công. Bạn có thể lấy id quá trình từ $pid.


3
OP muốn có được PID để anh ta có thể giết nó sau. ; và && yêu cầu quá trình ban đầu để thoát trước tiếng vang $! được thực thi.
dùng9517

Vâng, bạn đúng. Điều này sẽ cung cấp cho bạn pid sau khi myCommand đã chấm dứt.
Khaled

6
Tham chiếu $!sau &&hoặc ;sẽ không bao giờ cung cấp cho bạn PID của quá trình bắt đầu cho phía bên trái của dấu phân cách lệnh. $!chỉ được đặt cho các quy trình được khởi chạy không đồng bộ (ví dụ: thông thường &nhưng một số shell cũng có các phương thức khác).
Chris Johnsen

nó rất hữu ích và tại sao không ai bỏ phiếu cho tôi? mặc dù công việc tốt :)
chùa

1

Đây là một chút câu trả lời hack-y và sẽ không có khả năng làm việc cho hầu hết mọi người. Đây cũng là một vấn đề rủi ro bảo mật rất lớn, vì vậy đừng làm điều đó trừ khi bạn chắc chắn rằng bạn sẽ an toàn và các đầu vào được vệ sinh và ... tốt, bạn hiểu ý.

Biên dịch chương trình C nhỏ ở đây thành một nhị phân gọi là start(hoặc bất cứ thứ gì bạn muốn), sau đó chạy chương trình của bạn dưới dạng./start your-program-here arg0 arg1 arg2 ...

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    if (argc >= 2)
    {
        printf("%lu\n", (long unsigned) getpid());
        if (execvp(argv[1], &argv[1]) < 0)
        {
            perror(NULL);
            return 127;
        }
    }
    return 0;
}

Câu chuyện dài, điều này sẽ in ra PID stdout, sau đó tải chương trình của bạn vào quy trình. Nó vẫn phải có cùng một PID.


Làm cách nào tôi có thể ghi lại số PID được mã này trả về trong biến bash? Vì một lý do không rõ ràng với tôi, stdoutdường như không được ghi lại trong ví dụ này:RESULT="$(./start_and_get_pid.out echo yo)"; echo "$RESULT"
Tfb9

@ Tfb9: Tôi thành thật khuyên bạn nên một trong những cách tiếp cận khác; Tôi đã viết cái này hơn 2 năm trước và cho đến nay, một trong những phương pháp dễ bị hack / dễ bị lỗi (tôi nghĩ vậy).
tonysdg

Cảm ơn đã lưu ý. Tôi nghĩ rằng tôi đã giải quyết vấn đề của mình với điều nàysleep 10 & PID_IS=$!; echo $PID_IS
Tfb9
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.