$> set -o pipefail
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Повезло!
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Вы проиграли
ที่นี่ fortune
โปรแกรมแบบมีเงื่อนไขโดยไม่ต้อง exit(rand())
.
คุณสามารถอธิบาย? เกิดอะไรขึ้นที่นี่?
การพูดนอกเรื่องโคลงสั้น ๆ - ประวัติศาสตร์
ฉันคุ้นเคยกับ Heisenbug นี้เป็นครั้งแรกเมื่อหนึ่งในสี่ของศตวรรษที่ผ่านมา จากนั้นสำหรับเกตเวย์ใน FaxNET จำเป็นต้องสร้างยูทิลิตี้หลายอย่างผ่าน
ประสบการณ์ก่อนหน้าของฉันในการจัดการกับจุดบกพร่องใน sendmail และ uucp/uupc ได้เพิ่มความขยันหมั่นเพียรของฉันในเรื่อง “การจัดการข้อผิดพลาดอย่างละเอียด” ไม่มีประโยชน์ที่จะเจาะลึกรายละเอียดของเรื่องราวนั้น แต่ฉันต่อสู้กับ Heisenbug นี้เป็นเวลาสองสัปดาห์เป็นเวลา 10-14 ชั่วโมง จึงจำขึ้นใจและเมื่อวานคนรู้จักเก่าคนนี้ก็แวะมาเยี่ยมอีกครั้ง
TL; DR คำตอบ
คุณประโยชน์ head
สามารถ ปิดช่องจาก fortune
ทันที ทันทีที่เขาอ่านบรรทัดแรก ถ้า fortune
เอาต์พุตมากกว่าหนึ่งบรรทัด จากนั้นจึงเป็นการโทรที่สอดคล้องกัน write()
จะส่งคืนข้อผิดพลาดหรือรายงานว่าเอาต์พุตมีจำนวนไบต์น้อยกว่าที่ร้องขอ ในทางกลับกันเขียนด้วยการจัดการข้อผิดพลาดอย่างระมัดระวัง fortune
มีสิทธิที่จะสะท้อนสถานการณ์นี้ในสถานะทางออก แล้วเนื่องจากการติดตั้ง set -o pipefail
จะทำงาน || echo "Вы проиграли"
.
อย่างไรก็ตาม head
อาจจะมาไม่ทัน ปิดก่อน fortune
จะเสร็จสิ้นการส่งออกข้อมูล จากนั้นมันจะทำงาน && echo "Повезло!"
.
หนึ่งในวันนี้ของฉัน GNUMakefile
echo '#define MDBX_BUILD_COMPILER "$(shell set -o pipefail; $(CC) --version | head -1 || echo 'Please use GCC or CLANG compatible compiler')"'
แปลเป็นมนุษย์แล้ว
เป็นเรื่องปกติสำหรับที่นี่ --version
มันจะถามว่าเขาเป็นใคร และหากตัวเลือกนี้ไม่ได้รับการสนับสนุน ก็จะแทรกต้นขั้วเข้าไป "โปรดใช้คอมไพเลอร์ที่เข้ากันได้กับ GCC หรือ CLANG".
เช่น
#define MDBX_BUILD_COMPILER "lcc:1.23.20:Sep--4-2019:e2k-v3-linux Please use GCC or CLANG compatible compiler"
พูดตามตรงว่าฉันจำ "คนรู้จัก" เก่าของฉันในทันทีไม่ได้ นอกจากนี้ โปรเจ็กต์นี้ได้รับการทดสอบหลายครั้งบน Elbrus และภายใต้การแจกแจงที่แตกต่างกันมากมาย รวมถึง Alt ด้วยคอมไพเลอร์ที่หลากหลาย เวอร์ชันของ GNU Make และ bash ดังนั้นฉันไม่ต้องการที่จะเห็นความผิดพลาดของฉันที่นี่
เมื่อพยายามจำลองปัญหาและ/หรือทำความเข้าใจกับสิ่งที่เกิดขึ้น สิ่งแปลกๆ ก็เริ่มเกิดขึ้น
สะกดบรรทัดคำสั่ง:
echo "#define MDBX_BUILD_COMPILER '$(set -o pipefail; LC_ALL=C cc --version | head -1 || echo "Please use GCC or CLANG compatible compiler")'"
บางครั้งมันจะสร้างข้อความเพิ่มเติม แต่ก็ไม่... บ่อยครั้งที่ตัวเลือกใดตัวเลือกหนึ่งจะอยู่ได้ค่อนข้างนาน แต่ถ้าคุณกระตุ้นนานกว่านี้ คุณจะได้ทั้งสองอย่างเสมอ!
แน่นอน strace
โดยวิธีการเช่นเดียวกับการเคารพตนเอง strace
ไม่ชอบที่จะสืบพันธุ์
เกิดอะไรขึ้น?
- คุณประโยชน์
head
มีสิทธิ์ (หรือมากกว่านั้นคือถูกบังคับ) เพื่อปิดช่องที่กำลังอ่านทันทีที่อ่านตามจำนวนบรรทัดที่ร้องขอ - ผู้เขียนโปรแกรมสร้างข้อมูล (ในกรณีนี้คือ
cc
) สามารถ พิมพ์หลายบรรทัดและ ฟรี ทำสิ่งนี้ผ่านการโทรหลายครั้งwrite()
. - ถ้า ผู้อ่านจะมีเวลาปิดช่องฝั่งตนก่อนจบการบันทึกฝั่งผู้เขียน จากนั้น ผู้เขียนจะได้รับข้อผิดพลาด
- โปรแกรมนักเขียน สิทธิที่จะ ทั้งสองละเว้นข้อผิดพลาดในการเขียนช่องและสะท้อนให้เห็นในโค้ดที่สมบูรณ์ของคุณ
- เนื่องจากการติดตั้ง
set -o pipefail
รหัสการเสร็จสิ้นไปป์ไลน์จะไม่เป็นศูนย์ (ผิดพลาด) หากผลลัพธ์ไม่เป็นศูนย์จากองค์ประกอบอย่างน้อยหนึ่งรายการ จากนั้นจึงจะทำงานได้|| echo "Please use GCC or CLANG compatible compiler"
.
อาจมีการเปลี่ยนแปลงได้ขึ้นอยู่กับว่าโปรแกรมตัวเขียนทำงานกับสัญญาณอย่างไร ตัวอย่างเช่น โปรแกรมอาจยุติการทำงานอย่างผิดปกติ (ด้วยการสร้างสถานะการยกเลิกที่ไม่ใช่ศูนย์/ข้อผิดพลาดโดยอัตโนมัติ) หรือ write()
จะส่งคืนผลลัพธ์ของการเขียนไบต์น้อยกว่าที่ร้องขอและตั้งค่า errno = EPIPE
.
ใครผิด?
ในกรณีที่อธิบายไว้ เล็กน้อยของทุกสิ่ง. เกิดข้อผิดพลาดในการจัดการใน cc
(lcc:1.23.20:Sep—4-2019:e2k-v3-linux) ไม่ใช่ ซ้ำซ้อน ในหลายกรณี เป็นการดีกว่าถ้าทำผิดโดยระมัดระวัง แม้ว่าจะเผยให้เห็นข้อบกพร่องอย่างกะทันหันในสำเร็จรูปที่ออกแบบมาเพื่อพฤติกรรมแบบดั้งเดิมก็ตาม
จะทำอย่างไร?
ไม่ถูก:
fortune | head -1 && echo "Повезло, но вы рискуете!" || echo "WTF?"
แก้ไข:
-
(fortune && echo "Успешно" || echo "Ошибка") | head -1
ที่นี่ การปิดไปป์ก่อนกำหนดจะได้รับการจัดการโดยล่ามคำสั่ง เมื่อให้บริการไปป์ไลน์ที่ซ้อนกัน ("ภายใน" วงเล็บ) ตามนั้น ถ้า
fortune
จะรายงานข้อผิดพลาดเป็นลายลักษณ์อักษรไปยังช่องปิดในสถานะแล้วเอาต์พุต|| echo "Ошибка"
ไปไม่ถึงไหนเพราะปิดช่องไปแล้ว -
fortune | cat - | head -1 && echo "Успешно" || echo "Ошибка"
นี่คือยูทิลิตี้
cat
ทำหน้าที่เป็นแดมเปอร์เพราะจะละเว้นข้อผิดพลาดEPIPE
เมื่อถอนตัว แค่นี้ก็เพียงพอแล้วสำหรับการสรุปfortune
เล็ก (หลายบรรทัด) และพอดีกับบัฟเฟอร์ช่องสัญญาณ (ตั้งแต่ 512 ไบต์ถึง µ64K ใน OS ส่วนใหญ่ ≥4K) มิฉะนั้นปัญหาอาจกลับมา
วิธีการประมวลผลอย่างถูกต้อง EPIPE
และข้อผิดพลาดในการบันทึกอื่น ๆ ?
ไม่มีวิธีแก้ปัญหาที่ถูกต้องเพียงวิธีเดียว แต่มีคำแนะนำง่ายๆ:
EPIPE
ต้อง จะต้องได้รับการประมวลผล (และสะท้อนให้เห็นในสถานะการออก) เมื่อส่งออกข้อมูลที่ต้องใช้ความสมบูรณ์ ตัวอย่างเช่นระหว่างการทำงานของผู้จัดเก็บหรือยูทิลิตี้สำรองข้อมูลEPIPE
ดีกว่าที่จะเพิกเฉย เมื่อแสดงข้อมูลและข้อความเสริม เช่น เมื่อแสดงข้อมูลเกี่ยวกับตัวเลือกต่างๆ--help
หรือ--version
.- หากโค้ดที่กำลังพัฒนาสามารถนำไปใช้ในไปป์ไลน์ก่อนได้
| head
แล้วEPIPE
เป็นการดีกว่าที่จะเพิกเฉย ไม่เช่นนั้นจะเป็นการดีกว่าที่จะประมวลผลและสะท้อนถึงสถานะการออก
ผมขอใช้โอกาสนี้แสดงความขอบคุณทีมงาน
สู้ต่อไป สู้ ๆ นะ
ขอบคุณ
เคดีพีวีจาก
ที่มา: will.com