ทำไมตัวแปรจึงมองเห็นได้ใน subshell


18

Learning Bash Book กล่าวถึงว่า subshell จะสืบทอดเฉพาะตัวแปรสภาพแวดล้อมและ file descriptors ฯลฯ และจะไม่รับสืบทอดตัวแปรที่ไม่ได้ถูกส่งออก:

$ var=15
$ (echo $var)
15
$ ./file # this file include the same command echo $var

$

ฉันรู้ว่าเชลล์จะสร้างสอง subshell สำหรับ()และ for ./fileแต่ทำไมใน()กรณีที่ subshell ระบุvarตัวแปรแม้ว่าจะไม่ถูกส่งออกและใน./fileกรณีที่มันไม่ได้ระบุมัน?

# Strace for () 
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f24558b1a10) = 25617
# Strace for ./file
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f24558b1a10) = 25631

ฉันพยายามใช้straceเพื่อหาว่าสิ่งนี้เกิดขึ้นอย่างไรและฉันพบว่า bash จะใช้อาร์กิวเมนต์เดียวกันสำหรับการเรียกระบบโคลนดังนั้นนี่หมายความว่าทั้งกระบวนการแยก()และ./fileควรมีพื้นที่ที่อยู่กระบวนการเดียวกันของผู้ปกครองดังนั้นทำไม ใน()กรณีที่เป็น varible มองเห็นได้กับ subshell และไม่เกิดขึ้นสำหรับ./fileกรณีแม้ว่าข้อโต้แย้งเดียวกันจะขึ้นอยู่กับการเรียกระบบโคลน?


vinc17 พูดจริงแม้คุณได้รับ pstree เมื่อคุณมี subshell คุณเชื่อเรื่องนี้
PersianGulf

คำตอบ:


15

สมุดการเรียนรู้ Bash ผิด Subshells สืบทอดตัวแปรทั้งหมด แม้$$(PID ของเปลือกเดิม) จะถูกเก็บไว้ เหตุผลก็คือสำหรับเชลล์ย่อยเชลล์จะแยกและไม่เรียกใช้เชลล์ใหม่ (ในทางกลับกันเมื่อคุณพิมพ์./fileคำสั่งใหม่จะถูกดำเนินการเช่นเชลล์ใหม่ในเอาต์พุต strace ดูexecveและคล้ายกัน) . ดังนั้นโดยทั่วไปมันเป็นเพียงแค่การคัดลอก

หมายเหตุ: นี่ไม่ใช่การทุบตีโดยเฉพาะ นี่เป็นเรื่องจริงสำหรับเชลล์ใด ๆ


Oky แต่ตอนนี้ฉันพยายาม lauch strace บนเชลล์และฉันพยายามเรียกใช้. / file แต่ฉันไม่พบการเรียก exec และดังนั้นพื้นที่ที่อยู่ควรจะเหมือนกันสำหรับทั้งสองกระบวนการดังนั้นวิธีนี้สามารถอธิบายได้อย่างไร
3718463

@ user3718463 คุณใช้-fตัวเลือกในstraceการติดตามเด็ก ๆ ด้วยหรือไม่ นั่นเป็นสิ่งจำเป็นในการค้นหาผู้บริหารของ
vinc17

ใช่ฉันคิดออกขอบคุณมากฉันก็หายไปตัวเลือก -f และดังนั้นฉันไม่สามารถหา exec sys โทร
user3718463

16

คุณหรือหนังสือทำให้เกิดความสับสนกับ subshell ด้วย subprocess นั่นคือเชลล์

เชลล์บางตัวสร้างผลลัพธ์ในเชลล์ฟอร์กกระบวนการชายด์ ภายใต้ Linux forkเป็นกรณีพิเศษของการcloneเรียกระบบทั่วไปซึ่งคุณสังเกตเห็นในstraceบันทึก เด็กรันส่วนหนึ่งของเชลล์สคริปต์ กระบวนการเด็กเรียกว่าsubshell สิ่งก่อสร้างที่ตรงที่สุดคือcommand1 &: command1ทำงานใน subshell และคำสั่งที่ตามมาจะทำงานใน parent parent โครงสร้างอื่น ๆ ที่สร้าง subshell รวมถึงการทดแทนคำสั่ง$(command2)และไพพ์command3 | command4( command3ทำงานใน subshell, command4ทำงานใน subshell ในเชลล์ส่วนใหญ่ แต่ไม่ใช่ใน ksh หรือ zsh)

เชลล์ย่อยเป็นสำเนาของกระบวนการพาเรนต์ดังนั้นจึงไม่เพียง แต่มีตัวแปรสภาพแวดล้อมเดียวกันเท่านั้น แต่ยังรวมถึงคำจำกัดความภายในที่เหมือนกันทั้งหมด: ตัวแปร (รวมถึง$$ID กระบวนการของกระบวนการเชลล์เดิม), ฟังก์ชั่น, นามแฝง, ตัวเลือก ฯลฯ ก่อนเรียกใช้งานโค้ดในเชลล์ย่อย bash จะตั้งค่าตัวแปรBASHPIDเป็น ID กระบวนการของกระบวนการลูก

เมื่อคุณรัน./fileสิ่งนี้จะเรียกใช้งานคำสั่งภายนอก ก่อนอื่นเชลล์จะจัดการกับกระบวนการลูก ดังนั้นกระบวนการลูกนี้เรียกใช้งาน (ด้วยการexecveเรียกระบบ) ไฟล์เรียก./fileทำงาน กระบวนการลูกสืบทอดคุณสมบัติกระบวนการของผู้ปกครองของมัน: สภาพแวดล้อมไดเรกทอรีปัจจุบัน ฯลฯ ด้านภายในของแอปพลิเคชันจะหายไปในการexecveโทร: ตัวแปรที่ไม่ได้ส่งออกฟังก์ชั่น ฯลฯ เป็นความคิดทุบตีที่เคอร์เนลไม่ทราบและ พวกเขาจะหายไปเมื่อทุบตีรันโปรแกรมอื่น แม้ว่าโปรแกรมอื่นนั้นจะเป็นสคริปต์ทุบตี แต่ก็มีการดำเนินการโดยอินสแตนซ์ใหม่ของการทุบตีที่ไม่ทราบหรือไม่สนใจว่ากระบวนการหลักของมันเกิดขึ้นเพื่อเป็นตัวอย่างของการทุบตี ดังนั้นตัวแปรเปลือก (ไม่ใช่ส่งออกตัวแปร) execveไม่รอด


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

@ flow2k สคริปต์ (เช่นโปรแกรม) ที่เชลล์กำลังตีความ
Gilles 'ดังนั้นหยุดความชั่วร้าย'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.