วิธีรับ pid ของกระบวนการที่เพิ่งเริ่มต้น


70

ฉันต้องการเริ่มต้นกระบวนการ (เช่น myCommand) และรับ pid (เพื่ออนุญาตให้ฆ่าได้ในภายหลัง)

ฉันลองใช้ ps และตัวกรองตามชื่อ แต่ฉันไม่สามารถแยกความแตกต่างของชื่อได้

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

เพราะชื่อกระบวนการไม่ซ้ำกัน

ฉันสามารถเรียกใช้กระบวนการโดย:

myCommand &

ฉันพบว่าฉันสามารถรับ PID นี้ได้โดย:

echo $!

มีวิธีแก้ไขที่ง่ายกว่านี้ไหม?

ฉันยินดีที่จะรัน myCommand และรับ PID ของมันจากคำสั่งหนึ่งบรรทัด

คำตอบ:


77

สิ่งที่สามารถจะง่ายกว่าecho $!? เป็นหนึ่งบรรทัด:

myCommand & echo $!

ขอบคุณที่รวมคำสั่งเหล่านี้เข้ากับ "&" ช่วยฉันได้มาก
rafalmag

8
ในสคริปต์ทุบตีในวงซึ่งเริ่มโปรแกรม $! ไม่ถูกต้อง บางครั้งมันก็ส่งคืน pid ของสคริปต์เองบางครั้ง grep หรือ awk รันจากสคริปต์เพื่อแก้ปัญหาใด ๆ โดยเฉพาะเพื่อให้ได้ pid ของกระบวนการที่เพิ่งเปิดตัวในสถานการณ์นี้ สิ่งที่ชอบ pid = myprogramน่ากลัว

2
หมายเหตุว่าสิ่งนี้ต้องการให้คุณเริ่มคำสั่งโดยใช้&บรรทัดก่อนหน้ามิฉะนั้นเสียงสะท้อนจะส่งคืนค่าว่าง
rogerdpack

2
กำหนดให้กับตัวแปรเช่นcommand & echo $!ค้างการดำเนินการในขั้นตอนนี้ :(
Shashank Vivek


20

คุณสามารถใช้sh -cและexecรับ PID ของคำสั่งก่อนที่มันจะรัน

ในการเริ่มต้นmyCommandเพื่อให้ PID ถูกพิมพ์ก่อนที่จะเริ่มทำงานคุณสามารถใช้:

sh -c 'echo $$; exec myCommand'

มันทำงานอย่างไร:

สิ่งนี้จะเริ่มเชลล์ใหม่พิมพ์ PID ของเชลล์นั้นจากนั้นใช้execbuiltin เพื่อแทนที่เชลล์ด้วยคำสั่งของคุณเพื่อให้แน่ใจว่าเชลล์นั้นมี PID เดียวกัน เมื่อเชลล์ของคุณรันคำสั่งด้วยexecbuiltin เชลล์ของคุณจะกลายเป็นคำสั่งนั้นจริง ๆ แล้วไม่ใช่พฤติกรรมทั่วไปของการปลอมสำเนาใหม่ของตัวเองซึ่งมี PID แยกต่างหากและจะกลายเป็นคำสั่ง

ฉันพบนี้ให้มากง่ายกว่าทางเลือกอื่นที่เกี่ยวข้องกับการดำเนินการไม่ตรงกัน (กับ&) psการควบคุมงานหรือการค้นหาด้วย วิธีการเหล่านั้นดี แต่ถ้าคุณไม่มีเหตุผลเฉพาะที่จะใช้ตัวอย่างเช่นบางทีคำสั่งกำลังทำงานอยู่ซึ่งในกรณีนี้การค้นหา PID หรือการใช้การควบคุมงานจะสมเหตุสมผล - ฉันขอแนะนำให้พิจารณาวิธีนี้ก่อน (และแน่นอนฉันจะไม่พิจารณาการเขียนสคริปต์ที่ซับซ้อนหรือโปรแกรมอื่น ๆ เพื่อให้บรรลุนี้)

คำตอบนี้รวมถึงตัวอย่างของเทคนิคนี้


บางส่วนของคำสั่งนั้นอาจถูกตัดออกไปเป็นบางครั้ง

แม้ว่าเชลล์ที่คุณใช้เป็นสไตล์ Bourne และสนับสนุนexecbuiltin ด้วยซีแมนทิกส์เหล่านี้คุณไม่ควรพยายามหลีกเลี่ยงการใช้sh -c(หรือเทียบเท่า) เพื่อสร้างกระบวนการเชลล์ใหม่ที่แยกต่างหากสำหรับวัตถุประสงค์นี้เนื่องจาก:

  • เมื่อเชลล์กลายเป็นmyCommandแล้วจะไม่มีเชลล์รอให้รันคำสั่งที่ตามมา sh -c 'echo $$; exec myCommand; fooจะไม่สามารถที่จะพยายามที่จะเรียกใช้หลังจากเปลี่ยนตัวเองด้วยfoo myCommandนอกจากว่าคุณกำลังเขียนสคริปต์ที่รันคำสั่งนี้เป็นคำสั่งสุดท้ายคุณจะไม่สามารถใช้echo $$; exec myCommandเชลล์ที่คุณใช้คำสั่งอื่นได้
  • คุณไม่สามารถใช้เชลล์ย่อยสำหรับสิ่งนี้ได้ (echo $$; exec myCommand)อาจจะดีกว่า syntactically กว่าsh -c 'echo $$; exec myCommand'แต่เมื่อคุณทำงาน$$ภายใน( )มันจะให้ PID ของเชลล์พาเรนต์ไม่ใช่ของเชลล์ย่อยเอง แต่มันเป็น PID ของ subshell ที่จะเป็น PID ของคำสั่งใหม่ เชลล์บางตัวมีกลไกแบบพกพาสำหรับการค้นหา PID ของเชลล์ย่อยซึ่งคุณสามารถใช้สำหรับสิ่งนี้ โดยเฉพาะในทุบตี 4 , (echo $BASHPID; exec myCommand)ไม่ทำงาน

ท้ายที่สุดโปรดทราบว่าบางเชลล์จะดำเนินการปรับให้เหมาะสมโดยที่พวกเขาเรียกใช้คำสั่งราวกับว่าโดยexec(กล่าวคือพวกเขาจะละทิ้งก่อน) เมื่อทราบว่าเชลล์ไม่จำเป็นต้องทำอะไรหลังจากนั้น เชลล์บางตัวพยายามทำสิ่งนี้ทุกครั้งที่มันเป็นคำสั่งสุดท้ายที่จะรันในขณะที่บางคำสั่งจะทำเมื่อไม่มีคำสั่งอื่น ๆ ก่อนหรือหลังคำสั่งและอื่น ๆ จะไม่ทำเลย ผลที่ได้คือถ้าคุณลืมที่จะเขียนexecและใช้เพียงบางครั้งsh -c 'echo $$; myCommand'มันจะให้ PID ที่ถูกต้องในบางระบบที่มีเชลล์บางตัว ฉันขอแนะนำไม่ให้พึ่งพาพฤติกรรมดังกล่าวและเสมอรวมถึงเมื่อเป็นสิ่งที่คุณต้องการexec


ก่อนที่ฉันจะเรียกใช้myCommandฉันต้องตั้งค่าตัวแปรสภาพแวดล้อมจำนวนหนึ่งในสคริปต์ทุบตีของฉัน สิ่งเหล่านี้จะส่งต่อไปยังสภาพแวดล้อมที่execคำสั่งกำลังทำงานอยู่หรือไม่?
user5359531

ดูเหมือนว่าสภาพแวดล้อมของฉันมีอยู่ในexecคำสั่ง อย่างไรก็ตามวิธีนี้ใช้ไม่ได้เมื่อmyCommandเริ่มต้นกระบวนการอื่นซึ่งเป็นสิ่งที่คุณต้องทำงานด้วย; เมื่อฉันออกkill -INT <pid>ที่ที่pidได้รับด้วยวิธีนี้สัญญาณไม่ถึงกระบวนการย่อยที่เริ่มต้นmyCommandในขณะที่ถ้าฉันทำงานmyCommand ในเซสชั่นปัจจุบันและ Ctrl + C สัญญาณแพร่กระจายอย่างถูกต้อง
user5359531

1
ฉันลองสิ่งนี้ แต่ pid ของกระบวนการ myCommand ดูเหมือนว่าจะเป็นเอาต์พุต pid โดย echo $$ +1 ฉันกำลังทำอะไรผิดหรือเปล่า?
crobar

คำสั่งของฉันมีลักษณะเช่นนี้:sh -c 'echo $$; exec /usr/local/bin/mbdyn -f "input.file" -o "/path/to/outputdir" > "command_output.txt" 2>&1 &'
crobar

7

ฉันไม่ทราบวิธีแก้ปัญหาที่ง่ายกว่านี้ แต่ไม่ได้ใช้ $! ดีพอแล้ว? คุณสามารถกำหนดค่าให้กับตัวแปรอื่น ๆ ได้เสมอหากคุณต้องการในภายหลังตามที่คนอื่นพูด

ในฐานะที่เป็นบันทึกข้างแทนท่อจาก PS คุณสามารถใช้หรือpgreppidof


5

ใช้ exec จากสคริปต์ทุบตีหลังจากลงทะเบียน pid ไปยังไฟล์:

ตัวอย่าง:

สมมติว่าคุณมีสคริปต์ชื่อ "forever.sh" ที่คุณต้องการเรียกใช้ด้วย args p1, p2, p3

รหัสแหล่ง Forever.sh:

#!/bin/sh

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

สร้าง reaper.sh:

#!/bin/sh

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

เรียกใช้ตลอดไป. ผ่าน reaper.sh:

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

forever.sh ไม่ทำอะไรมากไปกว่าการบันทึกบรรทัดไปยัง syslog ทุกๆ 5 วินาที

ตอนนี้คุณมี pid ใน /var/run/forever.sh.pid

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

และตลอดไป. syslog grep:

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

คุณสามารถดูได้ในตารางกระบวนการ:

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
โอ้และ "oneliner": / bin / sh -c 'echo $$> / tmp / my.pid && โปรแกรมโปรแกรม args' &
237419

1
ที่จะต้องรักษาช่องว่างภายในในการขัดแย้งที่คุณควรจะใช้แทนexec "$@" exec $*ในทางเทคนิคสิ่งที่คุณต้องเก็บรักษาไม่ใช่ช่องว่าง แต่เกิดขึ้นของอักขระในพารามิเตอร์เชลล์ IFS (ซึ่งเป็นค่าเริ่มต้นไปที่ช่องว่างแท็บและขึ้นบรรทัดใหม่)
Chris Johnsen

จุดที่ถ่าย :)
user237419

ขอบคุณฉันไม่ทราบพารามิเตอร์ $$ มันมีประโยชน์มาก
rafalmag

3

ในเชลล์ bash ทางเลือกอื่น$!อาจเป็นjobs -pแบบในตัว ในบางกรณี!ในการ$!ได้รับการตีความโดยเปลือกก่อน (หรือแทน) การขยายตัวของตัวแปรที่นำไปสู่ผลที่ไม่คาดคิด

เช่นนี้จะไม่ทำงาน:

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

ในขณะนี้จะ:

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

ฉันคิดว่าคุณหมายถึง ((คำสั่งของคุณ) & jobs -p> / var / run / pidfile)
Dom

1

คุณสามารถใช้สิ่งที่ชอบ:

$ myCommand ; pid=$!

หรือ

$ myCommand && pid=$!

ทั้งสองคำสั่งสามารถเป็นข้อต่อใช้หรือ; &&ในกรณีที่สอง pid จะถูกตั้งค่าเฉพาะในกรณีที่คำสั่งแรกประสบความสำเร็จ คุณสามารถรับรหัสกระบวนการ$pidได้


3
OP ต้องการรับ PID เพื่อให้เขาสามารถฆ่าได้ในภายหลัง ; และ && ต้องการกระบวนการต้นฉบับเพื่อออกก่อน echo $! ถูกประหารชีวิต
user9517

ใช่คุณถูก. สิ่งนี้จะให้ pid ให้คุณหลังจากที่ myCommand สิ้นสุดลง
เลด

6
การอ้างอิง$!หลังจาก&&หรือ;จะไม่ให้ PID ของกระบวนการที่เริ่มต้นสำหรับด้านซ้ายมือของตัวคั่นคำสั่ง $!มีการตั้งค่าเฉพาะสำหรับกระบวนการที่เปิดใช้งานแบบอะซิงโครนัส (เช่นโดยปกติจะมี&แต่เชลล์บางตัวยังมีวิธีอื่น ๆ )
Chris Johnsen

มันมีประโยชน์และทำไมไม่มีใครโหวตให้ฉันดีกว่า ทำได้ดี แต่ :)
วิหาร

1

นี่เป็นคำตอบที่แฮ็กและเป็นไปได้สำหรับคนส่วนใหญ่ นอกจากนี้ยังเป็นความเสี่ยงด้านความปลอดภัยที่มีขนาดใหญ่ดังนั้นอย่าทำอย่างนั้นจนกว่าคุณจะแน่ใจว่าคุณปลอดภัยและมีการป้อนข้อมูลให้ถูกสุขลักษณะและ ... คุณจะได้รับแนวคิด

คอมไพล์โปรแกรม C ตัวน้อยที่นี่ให้เป็นไบนารี่ที่เรียกว่าstart(หรืออะไรก็ตามที่คุณต้องการ) จากนั้นเรียกใช้โปรแกรมของคุณเป็น./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;
}

เรื่องสั้นสั้นนี้จะพิมพ์ PID ไปstdoutแล้วโหลดโปรแกรมของคุณเข้าสู่กระบวนการ มันควรจะยังคงมี PID เดียวกัน


ฉันจะบันทึกหมายเลข PID ที่ส่งคืนโดยรหัสนี้ในตัวแปร bash ได้อย่างไร สำหรับเหตุผลที่ไม่ชัดเจนให้ฉันstdoutดูเหมือนจะไม่ถูกบันทึกไว้ในตัวอย่างนี้:RESULT="$(./start_and_get_pid.out echo yo)"; echo "$RESULT"
Tfb9

@ Tfb9: ฉันขอแนะนำวิธีอื่นอย่างใดอย่างหนึ่ง; ฉันเขียนสิ่งนี้เมื่อ 2 ปีที่แล้วและมันก็เป็นหนึ่งในวิธีการแฮ็กเกอร์ / ข้อผิดพลาดที่น่าสนใจที่นำเสนอ (หรือฉันคิดว่า)
tonysdg

ขอบคุณสำหรับการบันทึก ฉันคิดว่าฉันแก้ไขปัญหาของฉันด้วยสิ่งนี้sleep 10 & PID_IS=$!; echo $PID_IS
Tfb9
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.