ดังนั้นฉันจึงอยากให้คำตอบเหมือน Lesmana แต่ฉันคิดว่าของฉันอาจจะง่ายกว่าและบริสุทธิ์กว่าเล็กน้อยจาก Bourne-shell solution:
# You want to pipe command1 through command2:
exec 4>&1
exitstatus=`{ { command1; printf $? 1>&3; } | command2 1>&4; } 3>&1`
# $exitstatus now has command1's exit status.
ฉันคิดว่านี่เป็นคำอธิบายที่ดีที่สุดจากภายในออก - command1 จะดำเนินการและพิมพ์เอาต์พุตปกติบน stdout (file descriptor 1) จากนั้นเมื่อเสร็จแล้ว printf จะดำเนินการและพิมพ์รหัสทางออกของ icommand1 บน stdout แต่ stdout นั้นถูกเปลี่ยนเส้นทางไป ตัวอธิบายไฟล์ 3.
ในขณะที่ command1 กำลังทำงาน stdout ของมันจะถูกไพพ์ไปที่ command2 (เอาต์พุตของ printf ไม่ทำให้เป็น command2 เพราะเราส่งไปที่ file descriptor 3 แทนที่จะเป็น 1 ซึ่งเป็นสิ่งที่ pipe อ่าน จากนั้นเราเปลี่ยนเส้นทางเอาต์พุตของ command2 ไปยัง file descriptor 4 เพื่อให้มันยังคงอยู่จาก file descriptor 1 - เพราะเราต้องการ file descriptor 1 ฟรีอีกเล็กน้อยในภายหลังเพราะเราจะนำเอา printf บน file descriptor 3 1 - เพราะนั่นคือสิ่งที่การทดแทนคำสั่ง (backticks) จะจับภาพและนั่นคือสิ่งที่จะถูกวางลงในตัวแปร
บิตสุดท้ายของเวทมนตร์คือสิ่งแรก exec 4>&1
เราทำในฐานะคำสั่งแยกต่างหาก - มันเปิดไฟล์ descriptor 4 เป็นสำเนาของ stdout ของเปลือกนอก การทดแทนคำสั่งจะจับสิ่งที่เขียนบนมาตรฐานออกมาจากมุมมองของคำสั่งภายใน - แต่เนื่องจากเอาต์พุตของ command2 จะไปยังไฟล์ descriptor 4 เท่าที่เกี่ยวข้องกับการทดแทนคำสั่งการแทนที่คำสั่งจะไม่จับ - แต่เมื่อมัน ได้รับ "out" ของการทดแทนคำสั่งมันยังคงมีประสิทธิภาพไปที่ file descriptor โดยรวมของสคริปต์ 1
(ใน exec 4>&1
จะต้องมีคำสั่งแยกต่างหากเพราะเชลล์ทั่วไปจำนวนมากไม่ชอบเมื่อคุณพยายามเขียนไปยังไฟล์ descriptor ภายในการทดแทนคำสั่งที่เปิดในคำสั่ง "ภายนอก" ที่ใช้การทดแทนดังนั้นนี่คือ วิธีพกพาที่ง่ายที่สุดที่จะทำ)
คุณสามารถดูได้ในเชิงเทคนิคน้อยและขี้เล่นมากขึ้นราวกับว่าเอาต์พุตของคำสั่งกระโดดข้ามกัน: command1 ไพพ์ไปยัง command2 ดังนั้นเอาต์พุตของ printf ข้ามคำสั่ง 2 ดังนั้น command2 ไม่จับแล้ว เอาต์พุตของคำสั่ง 2 กระโดดข้ามไปและออกจากการทดแทนคำสั่งเช่นเดียวกับ printf ลงทันเวลาที่จะถูกดักจับโดยการทดแทนเพื่อให้มันจบลงในตัวแปรและเอาต์พุตของ command2 ดำเนินไปอย่างสนุกสนานเขียนไปยังเอาต์พุตมาตรฐานเช่นเดียวกับ ในท่อปกติ
นอกจากนี้ตามที่ฉันเข้าใจ$?
จะยังคงมีโค้ดส่งคืนของคำสั่งที่สองในไพพ์เนื่องจากการกำหนดตัวแปรการแทนที่คำสั่งและคำสั่งผสมนั้นโปร่งใสทั้งหมดในโค้ดส่งคืนของคำสั่งภายในดังนั้นสถานะการส่งคืนของ command2 ควรแพร่กระจายออกไป - สิ่งนี้และไม่ต้องกำหนดฟังก์ชั่นเพิ่มเติมคือเหตุผลที่ฉันคิดว่านี่อาจเป็นทางออกที่ดีกว่าที่ lesmana เสนอ
ตามที่ระบุใน lesmana lesmana อาจเป็นไปได้ว่าคำสั่ง 1 จะจบลงด้วยการใช้ file descriptors 3 หรือ 4 เพื่อให้มีประสิทธิภาพมากขึ้นคุณจะต้อง:
exec 4>&1
exitstatus=`{ { command1 3>&-; printf $? 1>&3; } 4>&- | command2 1>&4; } 3>&1`
exec 4>&-
โปรดทราบว่าฉันใช้คำสั่งผสมในตัวอย่างของฉัน แต่ใช้ subshells (การใช้( )
แทน{ }
จะใช้ได้แม้ว่าอาจจะมีประสิทธิภาพน้อยกว่า)
คำสั่งสืบทอดไฟล์ descriptor จากกระบวนการที่เรียกใช้ดังนั้นบรรทัดที่สองทั้งหมดจะสืบทอดไฟล์ descriptor สี่และคำสั่งผสมตามด้วย3>&1
จะสืบทอดสืบทอด descriptor ไฟล์สาม ดังนั้นการ4>&-
ทำให้แน่ใจว่าคำสั่งผสมภายในจะไม่สืบทอดไฟล์ descriptor สี่และ3>&-
จะไม่สืบทอดไฟล์ descriptor สามดังนั้น command1 รับ 'สะอาด' สภาพแวดล้อมมาตรฐานที่มากกว่า คุณสามารถเลื่อนด้านใน4>&-
ข้างๆ3>&-
แต่ฉันคิดว่าทำไมไม่ จำกัด ขอบเขตเท่าที่จะทำได้
ฉันไม่แน่ใจว่าบ่อยครั้งที่สิ่งต่าง ๆ ใช้ file descriptor สามหรือสี่โดยตรง - ฉันคิดว่าโปรแกรมส่วนใหญ่ใช้ syscalls ที่ส่งคืนไฟล์ descriptor ที่ไม่ได้ใช้ที่ไม่ได้ใช้แล้ว แต่บางครั้งโค้ดเขียนไปยัง file descriptor 3 โดยตรงฉัน เดา (ฉันนึกภาพออกว่าโปรแกรมกำลังตรวจสอบ file descriptor เพื่อดูว่ามันเปิดอยู่หรือไม่และใช้มันถ้ามันเป็นหรือทำงานต่างไปตามนั้นถ้าไม่ใช่) ดังนั้นสิ่งหลังน่าจะดีที่สุดที่คุณควรคำนึงถึงและใช้สำหรับกรณีทั่วไป