ทำไมการใช้ 'ใช่' บนท่อทุบตี * ไม่ * ทำให้เกิดลูปไม่สิ้นสุด?


16

ตามเอกสารของมันทุบตีรอจนกว่าคำสั่งทั้งหมดในไปป์ไลน์ได้เสร็จสิ้นการทำงานก่อนที่จะดำเนินการต่อไป

เชลล์รอคำสั่งทั้งหมดในไปป์ไลน์เพื่อยกเลิกก่อนที่จะส่งคืนค่า

เหตุใดคำสั่งจึงyes | trueเสร็จสิ้นในทันที ไม่ควรyesวนซ้ำตลอดไปและทำให้ท่อไม่เคยกลับมาใช่ไหม


และคำถามย่อย: ตามข้อมูลจำเพาะของ POSIXท่อส่งเชลล์อาจเลือกที่จะส่งคืนหลังจากคำสั่งสุดท้ายเสร็จสิ้นหรือรอจนกว่าคำสั่งทั้งหมดจะเสร็จสิ้น กระสุนทั่วไปมีพฤติกรรมแตกต่างกันในแง่นี้หรือไม่? มีกระสุนที่ไหนบ้างที่yes | trueจะวนซ้ำตลอดไป?


yes | tee >(true) >/dev/nullจะทำตามที่คุณคาดหวัง btw ตามที่teeดำเนินต่อไปจนกว่านักเขียนทุกคนจะตายดังนั้นการtrueออกจะไม่รบกวนมันอย่างสิ้นเชิง
Charles Duffy

1
trueโดยทั่วไปแล้วเป็น{return 0;}โปรแกรมดังนั้นฉันจะไม่คาดหวังว่ามันจะทำงานเป็นเวลานาน
Dmitry Grigoryev

คำตอบ:


33

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

หากคุณทำstrace yes | true1คุณจะเห็นเคอร์เนลเตรียมพร้อมสำหรับความเป็นไปได้ทั้งสองอย่าง:

write(1, "y\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\n"..., 4096) = -1 EPIPE (Broken pipe)
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=17556, si_uid=1000} ---
+++ killed by SIGPIPE +++

straceกำลังดูกิจกรรมผ่านดีบักเกอร์ API ซึ่งจะบอกก่อนเกี่ยวกับการเรียกระบบกลับมาพร้อมกับข้อผิดพลาดจากนั้นเกี่ยวกับสัญญาณ yesแม้ว่าจากมุมมองของสัญญาณจะเกิดขึ้นก่อน (ในทางเทคนิคสัญญาณจะถูกส่งหลังจากเคอร์เนลส่งคืนการควบคุมไปยังพื้นที่ผู้ใช้ แต่ก่อนที่จะดำเนินการคำสั่งเครื่องเพิ่มเติมดังนั้นwriteฟังก์ชัน "wrapper" ในไลบรารี C จะไม่ได้รับโอกาสในการตั้งค่าerrnoและกลับสู่แอปพลิเคชัน)


1น่าเศร้าที่straceเป็นลินุกซ์ที่เฉพาะเจาะจง Unixes ที่ทันสมัยส่วนใหญ่มีคำสั่งบางอย่างที่ทำสิ่งที่คล้ายกัน แต่มักจะมีชื่อที่แตกต่างกันมันอาจจะไม่ถอดรหัสอาร์กิวเมนต์ syscall เป็นอย่างละเอียดและบางครั้งมันก็ใช้งานได้สำหรับรูทเท่านั้น


3
@hugomg ในกรณีนั้นท่อไม่เกี่ยวข้องทั้งหมด
muru

3
@hugomg เนื่องจากไม่มีสิ่งใดyesเชื่อมต่อกับไพพ์
muru

4
มันเป็นความจริงนั่นคือการสาธิตพฤติกรรมที่ทำเป็นเอกสาร "รอจนกว่าคำสั่งทั้งหมดจะเสร็จสิ้นก่อนที่จะยุติการวางท่อ" มันเป็นเพียงการป้องกันไม่ให้yesได้รับ SIGPIPE เนื่องจาก FD ที่เขียนถึงไม่ได้เชื่อมต่อกับไพพ์
Tom Hunt

2
@hugomg มันวนซ้ำตลอดไปในลักษณะเดียวกับที่yes >/dev/nullวนซ้ำตลอดไป มันแสดงให้เห็นถึงอะไรเลยเกี่ยวกับท่อที่ไม่เป็นจริงของคำสั่งง่าย ๆ (เนื่องจากพฤติกรรมที่รอการยกเลิกทอมชี้ให้เห็นว่าเป็นจริงของคำสั่งง่าย ๆ เช่นกัน)
Charles Duffy

2
@zwol: ฉันคิดว่าเรากำลังใช้คำศัพท์ของเราที่มีความหมายแตกต่างกันเล็กน้อยที่นี่หรือคิดเกี่ยวกับสิ่งต่าง ๆ จากมุมมองที่แตกต่างกันเล็กน้อย ... แต่ในกรณีใดกรณีหนึ่งwrite()(ฟังก์ชั่นใน libc) จะไม่ส่งคืน ตัวจัดการสัญญาณทำงาน แต่เนื่องจากตัวจัดการสัญญาณสิ้นสุดโปรแกรมการควบคุมจะไม่ถูกถ่ายโอนและดังนั้นจึงwrite()ไม่กลับมา ใช่นั่นคือการใช้งานในเคอร์เนลโดยมีxxx_write()ฟังก์ชั่นบางอย่างกลับมา-EPIPEแต่เรากำลังแก้จุดบกพร่องโปรแกรมผู้ใช้พื้นที่และไม่สนใจในที่
Dietrich Epp

5

มีเปลือกหอยใดบ้างที่มี | จริงจะวนซ้ำไปตลอดกาล?

ไม่น่าเป็นไปได้เนื่องจากyesคำสั่งใช้ไพพ์และมันจะล้มเหลวเมื่อไพพ์ขาด sleepในทางกลับกันอย่าใช้ท่อดังนั้น:

sleep 100000000 | true

จะทำงานเป็นเวลา 100000000 วินาทีอย่างน้อย


2
ระวังเปลือกที่ทันสมัยทั้งหมดที่ไม่ได้แยกคำสั่ง builtin สุดท้าย (ขวาสุด) ในไพพ์และที่trueบิวด์อิน นี้ใช้กับรุ่นล่าสุดของBourne Shell, ,ksh93 zshหากคุณกดปุ่ม^Zเมื่อคำสั่งดังกล่าวกำลังทำงานสิ่งนี้จะหยุดการทำงานชั่วคราวและเชลล์จะไม่สามารถกู้คืนได้หากไม่มีความช่วยเหลือจากภายนอก
schily

3
zsh 4.3.4 (i386-pc-solaris2.11) ที่นี่ดังนั้นดูเหมือนว่าเพิ่งมีการแก้ไข แนวคิดที่น่าสนใจฉันจะต้องดูว่าฉันสามารถใช้การแก้ไขที่คล้ายกันสำหรับ Bourne Shell ได้หรือไม่ ยังคงมีคำถามว่ามันทำงานอย่างไรและกลุ่มกระบวนการ tty ใดถูกใช้เช่นเดียวกับใน Bourne Shell ข้อเท็จจริงที่ว่าคำสั่ง righmost เป็นบิลด์จะถูกค้นพบหลังจากกลุ่มกระบวนการสำหรับสลีปถูกตั้งค่าไว้แล้ว
schily

2
@CharlesDuffy จากสิ่งที่ฉันเข้าใจ schily รักษารุ่นของ sh ซึ่งเขา backport การปรับปรุงจากกระสุนที่ทันสมัย เขาโพสต์เกี่ยวกับที่นี่ที่ไหนสักแห่ง
muru

3
The Bourne เชลล์ในคลังมรดกสืบทอดถูกเก็บรักษาไว้จนกว่า ~ 2007 sbrk()แต่ไม่เคยถูกทำให้พกพาได้อย่างเต็มที่ในขณะที่มันยังคงมีการโทรไปยัง รุ่นพกพาและได้รับการบำรุงรักษาอยู่ในชุดเครื่องมือ schily และ @Charles Duffy ได้ค้นพบตำแหน่งสำหรับข้อมูลแล้ว ;-)
Schily

2
@muru คุณสมบัติหลายอย่างที่ฉันนำกลับไปที่ Bourne Shell นั้นมาจากbsh(Berthold Shell จาก VBERTOS ซึ่งเป็นหน่วยความจำเสมือนที่ได้รับการปรับปรุงจาก UNOS - UNIX clone แรก) Bsh ได้รับคุณสมบัติ csh มากมายในปี 1984 และ 1985 แต่กลไกนามแฝงจาก UNOS นั้นเหนือกว่าของ csh ในปี 1980 แล้ว คุณสมบัติใหม่ของ Bourne Shell มาจาก POSIX เพื่อให้เข้าใกล้ความสอดคล้องของ POSIX
Schily
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.