3>&4-
เป็นส่วนขยาย ksh93 ที่สนับสนุนโดย bash และสั้นสำหรับ3>&4 4>&-
นั่นคือ 3 ตอนนี้ชี้ไปที่ที่ 4 เคยเป็นและ 4 ถูกปิดแล้วดังนั้นสิ่งที่ชี้ไปที่ 4 ได้ย้ายไปที่ 3
การใช้งานทั่วไปจะเป็นในกรณีที่คุณทำซ้ำstdin
หรือstdout
บันทึกสำเนาและต้องการคืนค่าเช่นใน:
สมมติว่าคุณต้องการจับ stderr ของคำสั่ง (และ stderr เท่านั้น) ในขณะที่ปล่อย stdout ไว้คนเดียวในตัวแปร
การทดแทนคำสั่งvar=$(cmd)
สร้างไปป์ ปลายการเขียนของไปป์กลายเป็นcmd
stdout (file descriptor 1) และส่วนอื่น ๆ ถูกอ่านโดยเชลล์เพื่อเติมตัวแปร
ตอนนี้ถ้าคุณต้องการที่จะไปให้กับตัวแปรที่คุณสามารถทำ:stderr
var=$(cmd 2>&1)
ตอนนี้ทั้ง fd 1 (stdout) และ 2 (stderr) ไปที่ pipe (และในที่สุดก็ถึงตัวแปร) ซึ่งเป็นเพียงครึ่งหนึ่งของสิ่งที่เราต้องการ
ถ้าเราทำvar=$(cmd 2>&1-)
(ย่อvar=$(cmd 2>&1 >&-
) ตอนนี้cmd
stderr เท่านั้นไปที่ไปป์ แต่ fd 1 ถูกปิด หากcmd
พยายามที่จะเขียนเอาต์พุตใด ๆ ที่จะกลับมาพร้อมกับEBADF
ข้อผิดพลาดหากมันเปิดไฟล์มันจะได้รับ fd ฟรีครั้งแรกและไฟล์ที่เปิดจะได้รับมอบหมายให้stdout
เว้นแต่ว่าคำสั่งปกป้อง! ไม่ใช่สิ่งที่เราต้องการเช่นกัน
หากเราต้องการให้ stdout cmd
ถูกทิ้งไว้ตามลำพังนั่นคือชี้ไปที่ทรัพยากรเดียวกับที่ชี้ไปนอกการทดแทนคำสั่งดังนั้นเราจำเป็นต้องนำทรัพยากรนั้นไปใช้ในการทดแทนคำสั่ง เพื่อที่เราจะสามารถทำสำเนาstdout
นอกการทดแทนคำสั่งเพื่อนำเข้าไปข้างใน
{
var=$(cmd)
} 3>&1
ซึ่งเป็นวิธีที่สะอาดกว่าการเขียน:
exec 3>&1
var=$(cmd)
exec 3>&-
(ซึ่งมีประโยชน์ในการกู้คืน fd 3 แทนที่จะปิดในตอนท้าย)
จากนั้นเมื่อถึง{
(หรือexec 3>&1
) และสูงสุด}
ทั้ง fd 1 และ 3 ชี้ไปที่ทรัพยากรเดียวกัน fd 1 ชี้ไปที่เริ่มต้น fd 3 จะชี้ไปที่ทรัพยากรภายในการทดแทนคำสั่ง (การทดแทนคำสั่งจะเปลี่ยนเส้นทาง fd 1, stdout เท่านั้น) ดังนั้นสำหรับcmd
เรามี fds 1, 2, 3:
- ไปป์ที่ var
- มิได้ถูกแตะต้อง
- เช่นเดียวกับสิ่งที่ 1 คะแนนไปนอกการทดแทนคำสั่ง
หากเราเปลี่ยนเป็น:
{
var=$(cmd 2>&1 >&3)
} 3>&1-
จากนั้นมันจะกลายเป็น:
- เช่นเดียวกับสิ่งที่ 1 คะแนนไปนอกการทดแทนคำสั่ง
- ไปป์ที่ var
- เช่นเดียวกับสิ่งที่ 1 คะแนนไปนอกการทดแทนคำสั่ง
ตอนนี้เรามีสิ่งที่เราต้องการ: stderr ไปที่ pipe และ stdout นั้นไม่ถูกแตะต้อง อย่างไรก็ตามเรากำลังรั่วไหลที่ fd cmd
3
ในขณะที่คำสั่ง (ตามแบบแผน) ถือว่า fds 0 ถึง 2 เป็นแบบเปิดและเป็นอินพุตมาตรฐานเอาต์พุตและข้อผิดพลาด แต่จะไม่ถือว่า fds อื่น ๆ ส่วนใหญ่แล้วพวกเขาจะออกจาก fd 3 ที่ไม่มีใครแตะต้อง หากพวกเขาต้องการตัวอธิบายไฟล์อื่นพวกเขาจะทำสิ่งopen()/dup()/socket()...
ที่จะส่งกลับไฟล์อธิบายครั้งแรกที่มีอยู่ หาก (เช่นเชลล์สคริปต์ที่ทำexec 3>&1
) พวกเขาจำเป็นต้องใช้สิ่งนั้นfd
โดยเฉพาะพวกเขาจะกำหนดให้กับบางสิ่ง (และในกระบวนการนั้นทรัพยากรที่ถือโดย fd 3 ของเราจะถูกปล่อยโดยกระบวนการนั้น)
มันเป็นวิธีที่ดีที่ใกล้ fd 3 ตั้งแต่cmd
ไม่ได้ทำให้การใช้งานของมัน cmd
แต่มันไม่ใช่เรื่องใหญ่ถ้าเราปล่อยให้มันได้รับมอบหมายก่อนที่เราเรียกว่า ปัญหาอาจเกิดขึ้น: ที่cmd
(และกระบวนการอื่น ๆ ที่อาจเกิดขึ้น) นั้นมี fd น้อยลงหนึ่งตัว ปัญหาร้ายแรงที่อาจเกิดขึ้นคือถ้าทรัพยากรที่ fd ชี้ไปนั้นอาจจบลงด้วยกระบวนการที่เกิดcmd
ในพื้นหลัง อาจเป็นเรื่องที่น่ากังวลหากทรัพยากรนั้นเป็นช่องทางหรือช่องทางการสื่อสารระหว่างกระบวนการอื่น ๆ (เช่นเมื่อสคริปต์ของคุณถูกเรียกใช้script_output=$(your-script)
) เนื่องจากจะหมายถึงกระบวนการที่อ่านจากปลายอีกด้านหนึ่งจะไม่เห็นจุดสิ้นสุดของไฟล์จนกระทั่ง กระบวนการพื้นหลังยุติลง
ดังนั้นที่นี่จะดีกว่าที่จะเขียน:
{
var=$(cmd 2>&1 >&3 3>&-)
} 3>&1
ซึ่งด้วยbash
สามารถย่อให้:
{
var=$(cmd 2>&1 >&3-)
} 3>&1
เพื่อสรุปสาเหตุที่ใช้บ่อย:
- มันไม่ได้มาตรฐานและแค่น้ำตาลซินแทคติค คุณต้องสมดุลการบันทึกการกดแป้นบางครั้งด้วยการทำให้สคริปต์ของคุณพกพาได้น้อยลงและเห็นได้ชัดน้อยลงสำหรับคนที่ไม่คุ้นเคยกับคุณสมบัติที่ผิดปกตินั้น
- จำเป็นที่จะต้องปิด FD เดิมหลังจากการทำซ้ำมันก็มักจะมองข้ามเพราะส่วนใหญ่เวลาที่เราจะไม่ต้องทนทุกข์ทรมานจากผลที่ตามมาเพื่อให้เราเพียงแค่ทำ
>&3
แทนหรือ>&3-
>&3 3>&-
หลักฐานว่ามันไม่ค่อยได้ใช้ตามที่คุณพบคือว่ามันเป็นของปลอมในทุบตี ในทุบตีcompound-command 3>&4-
หรือany-builtin 3>&4-
leaves fd 4 ปิดแม้หลังจากcompound-command
หรือany-builtin
กลับมาแล้ว มีโปรแกรมแก้ไขเพื่อแก้ไขปัญหาในขณะนี้ (2013-02-19)