3>&4-เป็นส่วนขยาย ksh93 ที่สนับสนุนโดย bash และสั้นสำหรับ3>&4 4>&-นั่นคือ 3 ตอนนี้ชี้ไปที่ที่ 4 เคยเป็นและ 4 ถูกปิดแล้วดังนั้นสิ่งที่ชี้ไปที่ 4 ได้ย้ายไปที่ 3
การใช้งานทั่วไปจะเป็นในกรณีที่คุณทำซ้ำstdinหรือstdoutบันทึกสำเนาและต้องการคืนค่าเช่นใน:
สมมติว่าคุณต้องการจับ stderr ของคำสั่ง (และ stderr เท่านั้น) ในขณะที่ปล่อย stdout ไว้คนเดียวในตัวแปร
การทดแทนคำสั่งvar=$(cmd)สร้างไปป์ ปลายการเขียนของไปป์กลายเป็นcmdstdout (file descriptor 1) และส่วนอื่น ๆ ถูกอ่านโดยเชลล์เพื่อเติมตัวแปร
ตอนนี้ถ้าคุณต้องการที่จะไปให้กับตัวแปรที่คุณสามารถทำ:stderr var=$(cmd 2>&1)ตอนนี้ทั้ง fd 1 (stdout) และ 2 (stderr) ไปที่ pipe (และในที่สุดก็ถึงตัวแปร) ซึ่งเป็นเพียงครึ่งหนึ่งของสิ่งที่เราต้องการ
ถ้าเราทำvar=$(cmd 2>&1-)(ย่อvar=$(cmd 2>&1 >&-) ตอนนี้cmdstderr เท่านั้นไปที่ไปป์ แต่ 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 cmd3
ในขณะที่คำสั่ง (ตามแบบแผน) ถือว่า 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)