เมื่อใดที่จะใช้ () กับ {} ในการทุบตี?


74

ฉันกำลังศึกษาสคริปต์เปลือกทุบตีและฉันต้องการที่จะทราบความแตกต่างระหว่างและ(...) {...}หนึ่งจะเลือกระหว่างทั้งสองเมื่อเขียนสคริปต์ได้อย่างไร



3
คุณหมายถึงในบริบทของการจัดกลุ่มคำสั่งเท่านั้นหรือไม่
heemayl

คำตอบ:


87

หากคุณต้องการให้ผลข้างเคียงของรายการคำสั่งมีผลต่อเชลล์ปัจจุบันของคุณให้ใช้{...}
หากคุณต้องการยกเลิกผลข้างเคียงใด ๆ ให้ใช้(...)

ตัวอย่างเช่นฉันอาจใช้ subshell ถ้าฉัน:

  • ต้องการแก้ไข$IFSสำหรับคำสั่งบางอย่าง แต่ฉันไม่ต้องการที่จะเปลี่ยนแปลง$IFSทั่วโลกสำหรับเปลือกปัจจุบัน
  • cdอยู่ที่ไหนซักแห่ง แต่ฉันไม่ต้องการเปลี่ยน$PWDเปลือกปัจจุบัน

ควรสังเกตว่าสามารถใช้วงเล็บในการกำหนดฟังก์ชันได้:

  • การใช้งานปกติ: วงเล็บปีกกา: ร่างกายฟังก์ชั่นรันในเปลือกปัจจุบัน; ผลข้างเคียงยังคงอยู่หลังจากฟังก์ชั่นเสร็จสมบูรณ์

    $ count_tmp() { cd /tmp; files=(*); echo "${#files[@]}"; }
    $ pwd; count_tmp; pwd
    /home/jackman
    11
    /tmp
    $ echo "${#files[@]}"
    11    
    
  • การใช้งานที่ผิดปกติ: วงเล็บ: ร่างกายฟังก์ชั่นดำเนินการใน subshell; ผลข้างเคียงจะหายไปเมื่อออกจาก subshell

    $ cd ; unset files
    $ count_tmp() (cd /tmp; files=(*); echo "${#files[@]}")
    $ pwd; count_tmp; pwd
    /home/jackman
    11
    /home/jackman
    $ echo "${#files[@]}"
    0
    

เอกสาร


11
หลังจากหลายปีของการพัฒนาเชลล์ฉันไม่รู้ว่าคุณสามารถใช้วงเล็บเพื่อเรียกใช้ฟังก์ชั่นใน subshells เป็นความคิดที่ดีที่จะหลีกเลี่ยงการสร้างเนมสเปซทั่วโลก!
l0b0

7
การใช้localคำสำคัญจะช่วยขจัดมลพิษนั้นได้อีกต่อไป
เกล็นแจ็คแมน

2
ใช่ แต่คุณต้องจำไว้ว่าให้ประกาศตัวแปรท้องถิ่นทุกตัวและมันจะตัดโค้ด
l0b0

4
คำแนะนำ:หากคุณต้องการผลข้างเคียงฟรีฟังก์ชั่น แต่หลีกเลี่ยงไวยากรณ์ฟังก์ชั่นที่ผิดปกติการประกาศ (ซึ่งบรรณาธิการรหัสอาจจะไม่ได้ตระหนักถึง) แล้วเพียงแค่ใช้วงเล็บในการเรียกฟังก์ชั่นมากกว่าการประกาศ:pwd; (count_tmp); pwd;
ดรุณี

2
กับเชลล์ ... foo () (:;) เทียบเท่ากับ foo () {(:;); } นั่นคือวิธีที่รายงานหากคุณถาม!
แอนโธนี

23

จากเอกสารทุบตีอย่างเป็นทางการ:

()

( list )

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

{}

{ list; }

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


9

โค้ดใน '{}' ถูกเรียกใช้ในเธรด / กระบวนการ / สภาพแวดล้อมปัจจุบันและการเปลี่ยนแปลงจะถูกเก็บรักษาไว้เพื่อให้ชัดเจนยิ่งขึ้นรหัสจะถูกรันในขอบเขตปัจจุบัน
โค้ดใน '()' ถูกเรียกใช้ภายในกระบวนการลูกที่แยกต่างหากของการทุบตีที่ถูกทิ้งหลังจากประมวลผลแล้ว กระบวนการย่อยนี้มักถูกเรียกว่า sub-shell และอาจถูกมองว่าเป็นขอบเขตใหม่ที่เหมือนเด็ก

เป็นตัวอย่างให้พิจารณาดังต่อไปนี้ ...

 ~ # { test_var=test }
 ~ # echo $test_var
 test
 ~ # ( test_var2=test2 )
 ~ # echo $test_var2

 ~ # 

โปรดสังเกตในตัวอย่างแรกด้วย '{}' ตัวแปรยังคงถูกตั้งค่าแม้หลังจากปิด '}' ในขณะที่ในตัวอย่างด้วย '()' ตัวแปรจะไม่ถูกตั้งค่านอกขอบเขตของ '()'


4

(...)ใช้เพื่อเรียกใช้รหัสใน sub-shell โค้ดที่ใช้สิบสอง{...}จะไม่ถูกใช้ในเชลล์ย่อย

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