วงเล็บใส่คำสั่งลงใน subshell หรือไม่?


94

จากสิ่งที่ฉันได้อ่านการวางคำสั่งในวงเล็บควรเรียกใช้ใน subshell คล้ายกับการเรียกใช้สคริปต์ ถ้าเป็นจริงมันจะเห็นตัวแปร x ถ้า x ไม่ถูกส่งออกได้อย่างไร

x=1

การรัน(echo $x)บนบรรทัดคำสั่งจะส่งผลให้ 1

การรันecho $xสคริปต์จะไม่ส่งผลอะไรตามที่คาดไว้

คำตอบ:


134

เชลล์ย่อยเริ่มต้นเป็นสำเนาที่เหมือนกันเกือบของกระบวนการเชลล์เริ่มต้น ภายใต้ประทุนเปลือกเรียกforkระบบโทร1ซึ่งจะสร้างกระบวนการใหม่ที่มีรหัสและหน่วยความจำที่เป็นสำเนา2 เมื่อสร้างเชลล์ย่อยมีความแตกต่างน้อยมากระหว่างมันกับพาเรนต์ โดยเฉพาะพวกเขามีตัวแปรเดียวกัน แม้แต่$$ตัวแปรพิเศษยังคงค่าเดียวกันใน subshells: เป็นรหัสกระบวนการของเชลล์ดั้งเดิม ในทำนองเดียวกัน$PPIDเป็น PID ของผู้ปกครองของเปลือกเดิม

เชลล์บางตัวเปลี่ยนตัวแปรบางตัวในเชลล์ย่อย Bash ตั้งค่าBASHPIDเป็น PID ของกระบวนการเชลล์ซึ่งเปลี่ยนเป็น subshells Bash, zsh และ mksh จัด$RANDOMให้มีค่าที่แตกต่างกันในผู้ปกครองและใน subshell แต่นอกเหนือจากกรณีพิเศษในตัวเช่นนี้ตัวแปรทั้งหมดมีค่าเหมือนกันในเชลล์ย่อยในเชลล์ดั้งเดิมสถานะการเอ็กซ์พอร์ตเดียวกันสถานะอ่านอย่างเดียวเดียวกัน ฯลฯ นิยามฟังก์ชันทั้งหมดนิยาม alias และตัวเลือกเชลล์และ การตั้งค่าอื่น ๆ ได้รับการสืบทอดเช่นกัน

เชลล์ย่อยที่สร้างโดย(…)มีตัวให้คำอธิบายไฟล์เดียวกับผู้สร้าง วิธีการอื่นในการสร้าง subshells แก้ไขไฟล์ descriptor บางตัวก่อนเรียกใช้งานรหัสผู้ใช้ ตัวอย่างเช่นด้านซ้ายมือของไปป์วิ่งใน subshell 3กับเอาต์พุตมาตรฐานที่เชื่อมต่อกับไปป์ เชลล์ย่อยเริ่มต้นด้วยไดเรกทอรีปัจจุบันเดียวกัน, หน้ากากสัญญาณที่เหมือนกัน, ฯลฯ หนึ่งในข้อยกเว้นบางประการคือ subshells ไม่สืบทอดกับดักที่กำหนดเอง: ละเว้นสัญญาณ ( ) ยังคงถูกละเว้นใน subshell แต่กับดักอื่น ๆ ( SIGNAL ) ถูกรีเซ็ต เป็นการกระทำเริ่มต้น4 .trap '' SIGNALtrap CODE

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

ดังนั้น:

x=1
(echo $x)

พิมพ์1เนื่องจากเชลล์ย่อยเป็นการจำลองแบบของเชลล์ที่วางไว้

x=1
sh -c 'echo $x'

เกิดขึ้นกับการเรียกใช้เชลล์เป็นกระบวนการลูกของเชลล์ แต่xในบรรทัดที่สองไม่มีการเชื่อมต่อกับxบนบรรทัดที่สองมากกว่าใน

x=1
perl -le 'print $x'

หรือ

x=1
python -c 'print x'

1 ข้อยกเว้นคือksh93เปลือกหอยที่มีการปรับการฟอร์กให้เหมาะที่สุดและผลข้างเคียงส่วนใหญ่จะถูกจำลอง
2 ความหมายคือสำเนา จากมุมมองของการนำไปใช้งานมีการแบ่งปันเกิดขึ้นมากมาย
3 สำหรับด้านขวามันขึ้นอยู่กับเปลือก
4 หากคุณทดสอบสิ่งนี้โปรดทราบว่าสิ่งต่าง ๆ เช่น$(trap)อาจรายงานกับดักของเปลือกเดิม โปรดทราบว่ากระสุนจำนวนมากมีข้อบกพร่องในกรณีมุมที่เกี่ยวข้องกับกับดัก ตัวอย่างเช่นninjaljตั้งข้อสังเกตว่า ณ ทุบตี 4.3 bash -x -c 'trap "echo ERR at \$BASH_SUBSHELL \$BASHPID" ERR; set -E; false; echo one subshell; (false); echo two subshells; ( (false) )'เรียกใช้ERRกับดักจาก subshell ที่ซ้อนกันในกรณี "สอง subshells" แต่ไม่ใช่ERRกับดักจาก subshell กลาง - set -Eตัวเลือกควรเผยแพร่ERRกับดักทั้งหมด subshell แต่ subshell กลางได้รับการปรับให้เหมาะสมและดังนั้นจึงไม่ได้มีการเรียกใช้ERRกับดัก


2
@Kusalananda ไม่ ( x=out; (x=in; echo $x))
Gilles

2
@ flow2k นี่คือลำดับการขยายสำหรับสิ่งที่เกิดขึ้นในระดับเดียวกัน แต่คุณต้องพิจารณาว่าการขยายตัวนั้นผสมกับการประเมินผลอย่างไร เมื่อการขยายต้องการการประเมินของโครงสร้างที่ซ้อนกันโครงสร้างภายในจะถูกประเมินก่อน ตัวอย่างเช่นในการประเมินecho $(x=2; echo $x)ชิ้นส่วน$(x=2; echo $x)จะต้องมีการขยาย x=2; echo $xเรื่องนี้ต้องมีการประเมินคำสั่ง การขยายตัวของที่เกิดขึ้นในระหว่างการประเมินผลนี้หลังจากการประเมินผลส่วนหนึ่ง$x x=2
Gilles

2
@ flow2k ไม่มีคำสั่งระหว่างการขยายพารามิเตอร์และการทดแทนคำสั่ง โปรดทราบว่าประโยคนี้ใช้เครื่องหมายอัฒภาคเพื่อแยกขั้นตอนการขยาย แต่การขยายพารามิเตอร์และการทดแทนคำสั่งอยู่ในส่วนคำสั่งที่คั่นด้วยเครื่องหมายอัฒภาคเดียวกัน (ใช่มันบอบบาง) คำสั่งที่สำคัญเมื่อหนึ่งในส่วนที่มีผลข้างเคียงที่มีผลต่อส่วนอื่น ๆ เช่น (ด้วยการxตั้งค่า) หรือecho $(echo foo >somefile)${x-$(cat somefile)} echo $(echo $x),${x=1}
Gilles

1
@Gilles; ฉันสับสน. หากเชลล์ย่อยแตกต่างจากการใช้งานสคริปต์ดังนั้นทำไมจึงมีการกล่าวว่า: การเรียกใช้เชลล์สคริปต์จะเปิดใช้กระบวนการใหม่ซึ่งเป็นเชลล์ย่อย ? นอกจากนี้สภาพแวดล้อม subshell จะถูกสร้างขึ้นเป็นที่ซ้ำกันของสภาพแวดล้อมเปลือก ดังนั้น ./file จะถูกดำเนินการในสภาพแวดล้อม subshell ดังนั้นจึงควรสืบทอดพารามิเตอร์ของเชลล์ที่ตั้งค่าโดยการกำหนดตัวแปร
haccks

2
@haccks คำจำกัดความใน ABS นั้นเป็นการประมาณและไม่ใช่สิ่งที่ดีมาก ตัวอย่างเป็นสิ่งที่ดี แต่สองบรรทัดแรกของหน้านั้นเกินความจริงว่ามันผิด การเรียกใช้สคริปต์จากสคริปต์อื่นจะเป็นการเปิดใช้กระบวนการใหม่ซึ่งไม่ใช่ subshell ใน SUS คำจำกัดความนั้นถูกต้อง (แต่ไม่เข้าใจง่ายเสมอไป) ./fileไม่ได้ดำเนินการใน subshell ดูเพิ่มเติมที่unix.stackexchange.com/q/261638และunix.stackexchange.com/a/157962
Gilles

15

เห็นได้ชัดว่าใช่ตามที่เอกสารทั้งหมดกล่าวว่าคำสั่งวงเล็บถูกเรียกใช้ใน subshell

เปลือกย่อยสืบทอดสำเนาของตัวแปรทั้งหมดของผู้ปกครอง ความแตกต่างคือการเปลี่ยนแปลงใด ๆ ที่คุณทำใน subshell นั้นยังไม่ได้ทำใน parent

หน้าคน ksh ทำให้ชัดเจนกว่าทุบตีเล็กน้อย:

man ksh:

คำสั่งวงเล็บถูกดำเนินการในเปลือกย่อยโดยไม่ต้องลบตัวแปรที่ไม่ได้ส่งออก

man bash:

(รายการ)

รายการจะถูกดำเนินการในสภาพแวดล้อมของ subshell (ดูคำสั่งการดำเนินการด้านสิ่งแวดล้อมด้านล่าง) การกำหนดตัวแปรและคำสั่ง builtin ที่ส่งผลกระทบต่อสภาพแวดล้อมของเชลล์จะไม่มีผลบังคับใช้หลังจากคำสั่งเสร็จสิ้น

การปฏิบัติตามคำสั่งด้านสิ่งแวดล้อม

เชลล์มีสภาพแวดล้อมการประมวลผลซึ่งประกอบด้วยสิ่งต่อไปนี้: [... ] พารามิเตอร์เชลล์ที่ตั้งค่าโดยการกำหนดตัวแปร [... ]
การทดแทนคำสั่งคำสั่งที่จัดกลุ่มด้วยวงเล็บและคำสั่งแบบอะซิงโครนัสถูกเรียกใช้ในสภาพแวดล้อม subshell ที่ซ้ำซ้อนกับสภาพแวดล้อมของเชลล์ [... ]


3
นี่จะตรงกันข้ามกับWhen a simple command other than a builtin or shell function is to be executed, it is invoked in a separate execution environment that consists of the following.ซึ่งมีรายการ: · shell variables and functions marked for export, along with variables exported for the command, passed in the environment(จากman bashส่วนเดียวกัน) ซึ่งอธิบายว่าเหตุใดecho $x-script จึงไม่พิมพ์อะไรเลยหากxไม่ได้ถูกส่งออก
Johan E
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.