เหตุใด $$ จึงส่งคืนรหัสเดียวกันกับกระบวนการหลัก


160

ฉันมีปัญหากับ Bash และฉันไม่รู้ว่าทำไม
ภายใต้เปลือกฉันป้อน:

echo $$    ## print 2433
(echo $$)  ## also print 2433
(./getpid) ## print 2602

"getpid" เป็นโปรแกรม C เพื่อรับ pid ปัจจุบันเช่น:

   int main() {
    printf("%d", (int)getpid());
    return 0;
   }

สิ่งที่ทำให้ฉันสับสนคือ:

  1. ฉันคิดว่า "(คำสั่ง)" เป็นกระบวนการย่อย (ฉันใช่มั้ย) และฉันคิดว่า pid ของมันควรจะแตกต่างจาก pid ของผู้ปกครอง แต่พวกเขาเหมือนกันทำไม ...
  2. เมื่อฉันใช้โปรแกรมของฉันเพื่อแสดง pid ระหว่างวงเล็บ, pid ที่มันแสดงนั้นแตกต่างกันใช่ไหม?
  3. '$$' คล้ายแมโครหรือไม่

คุณสามารถช่วยฉันได้ไหม?


8
โปรดทราบว่าgetpidจะแสดง ID กระบวนการที่แตกต่างกันแม้ว่าจะไม่ได้ทำงานใน subshell
chepner

1
@Marian echo $$ $BASHPID ; ( echo $$ $BASHPID )แสดงให้เห็นว่ามันทำ วงเล็บเหลี่ยมจะสร้าง subshell คำสั่งอาจเปลี่ยนค่าตัวแปรและเชลล์พาเรนต์ต้องไม่เห็นการเปลี่ยนแปลงเหล่านั้น สิ่งนี้ถูกนำไปใช้เป็นการfork()ดำเนินการ
เบ็

คำตอบ:


223

$$ถูกกำหนดเพื่อส่งคืน ID กระบวนการของพาเรนต์ในเชลล์ย่อย จากหน้า man ภายใต้ "พารามิเตอร์พิเศษ":

$ ขยายเป็น ID กระบวนการของเชลล์ ในเชลล์ย่อย () จะขยายเป็น ID กระบวนการของเชลล์ปัจจุบันไม่ใช่เชลล์ย่อย

ในbash4 คุณจะได้รับกระบวนการ ID BASHPIDของเด็กด้วย

~ $ echo $$
17601
~ $ ( echo $$; echo $BASHPID )
17601
17634

16
"parent" เป็นสิ่งที่ทำให้เข้าใจผิดเล็กน้อย (อย่างน้อยก็เป็นของฉัน) จริง ๆ แล้วมันเป็น "ระดับสูงสุด" เชลล์ ตัวอย่างเช่น: echo $$; (echo $$; (echo $$))ก้อง pid เดียวกันสามครั้ง
Martin Bouladour

1
ขวา; ฉันควรจะบอกว่าค่านั้นสืบทอดมาจาก parent parent (ซึ่งสืบทอดค่าจากparent ของมันฯลฯ ) เชลล์ระดับบนสุดตั้งค่าเริ่มต้นแทนที่จะสืบทอดจากกระบวนการพาเรนต์ (ไม่ใช่เชลล์)
chepner

$ Expands to the process ID of the shellมันจะไหม echo $เพียงแค่สะท้อน $ ตามตัวอักษร
Alexander Mills

@AlexanderMills ดีใช่ $เพียงอย่างเดียวไม่ใช่การขยายพารามิเตอร์ man page อ้างถึงชื่อของพารามิเตอร์พิเศษซึ่งก็คือ$; มันไม่ได้อ้างว่า$ขยายอย่างเดียว
chepner

ตกลงฉันสุจริตไม่มีความคิดว่าสิ่งที่หมายถึง แต่echo $BASHPIDทำงานในทุบตี 4 และ 5 (แต่ไม่ใช่รุ่น 3.2.57 บน MacOS)
อเล็กซานเดอร์มิลส์

81

คุณสามารถใช้วิธีใดวิธีหนึ่งต่อไปนี้

  • $! เป็น PID ของกระบวนการพื้นหลังที่ผ่านมา
  • kill -0 $PID ตรวจสอบว่ามันยังทำงานอยู่หรือไม่
  • $$ เป็น PID ของเชลล์ปัจจุบัน

2
สัญลักษณ์แสดงหัวข้อที่สองไม่ควรเป็นkill -0 $!ถ้าเรากำลังพูดถึงกระบวนการพื้นหลัง? PIDไม่ได้ตั้งค่าเป็นอะไร
Isaac Freeman

26
  1. วงเล็บเรียกsubshell ในทุบตี เนื่องจากเป็นเพียง subshell จึงอาจมี PID เดียวกัน - ขึ้นอยู่กับการนำไปใช้งาน
  2. โปรแกรม C ที่คุณเรียกใช้นั้นเป็นกระบวนการแยกต่างหากซึ่งมี PID ที่เป็นเอกลักษณ์ของตัวเอง - ไม่สำคัญว่ามันจะอยู่ใน subshell หรือไม่
  3. $$เป็นนามแฝงในทุบตีเพื่อเป็นกําสคริปต์ปัจจุบัน ดูความแตกต่างระหว่าง$$และ$BASHPIDที่นี่และด้านบนขวาว่าตัวแปรเพิ่มเติม$BASH_SUBSHELLซึ่งมีระดับการซ้อน


2

หากคุณถามว่าจะรับ PID ของคำสั่งที่รู้จักได้อย่างไรมันจะคล้ายกับสิ่งนี้:

หากคุณออกคำสั่งด้านล่าง # คำสั่งที่ออกให้นั้นคือ ***

dd if = / dev / diskx ของ = / dev / disky


จากนั้นคุณจะใช้:

PIDs=$(ps | grep dd | grep if | cut -b 1-5)

สิ่งที่เกิดขึ้นที่นี่คือมันไปป์ทั้งหมดที่จำเป็นต้องใช้อักขระที่ไม่ซ้ำกับเขตข้อมูลและเขตข้อมูลนั้นสามารถสะท้อนโดยใช้

echo $ PID


2

วิธีนี้เป็นวิธีเดียวในการรับ pid ที่ถูกต้อง

pid=$(cut -d' ' -f4 < /proc/self/stat)

ดีเหมือนกันทำงานให้ย่อย

SUB(){
    pid=$(cut -d' ' -f4 < /proc/self/stat)
    echo "$$ != $pid"
}

echo "pid = $$"

(SUB)

ตรวจสอบผลลัพธ์

pid = 8099
8099 != 8100

เป็นความคิดที่ดี แต่นั่นจะไม่ช่วยให้คุณได้รับ pid ของ fork ที่เชลล์รันเพื่อดักจับเอาต์พุตของการตัดหรือไม่? ถ้าฉันเรียกใช้สองครั้งหนึ่งครั้งด้วย echo $ (... ) และอีกครั้งโดยไม่มีฉันก็จะได้คำตอบที่ต่างออกไป
Martin Dorey
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.