stdin
, stdout
และstderr
มีลำธารที่แนบมากับอธิบายไฟล์ 0, 1 และ 2 ตามลำดับของกระบวนการ
ที่พรอมต์ของเปลือกโต้ตอบในจำลอง terminal หรือขั้วทุกคนที่ 3 อธิบายไฟล์จะอ้างถึงเดียวกันคำอธิบายเปิดไฟล์ซึ่งจะได้รับโดยการเปิดแฟ้มอุปกรณ์ปลายทางหรือหลอกขั้ว (สิ่งที่ต้องการ/dev/pts/0
) ใน + เขียนอ่าน โหมด.
หากจากเปลือกโต้ตอบคุณเริ่มต้นสคริปต์ของคุณโดยไม่ต้องใช้การเปลี่ยนเส้นทางสคริปต์ของคุณจะสืบทอดไฟล์ descriptors เหล่านั้น
บน Linux /dev/stdin
, /dev/stdout
, /dev/stderr
มีการเชื่อมโยงสัญลักษณ์/proc/self/fd/0
, /proc/self/fd/1
, /proc/self/fd/2
ตามลำดับตัวเอง symlinks พิเศษไปยังแฟ้มจริงที่เปิดให้บริการในการอธิบายไฟล์เหล่านั้น
พวกเขาไม่ใช่ stdin, stdout, stderr เป็นไฟล์พิเศษที่ระบุว่า stdin, stdout, stderr ไปที่ใด (โปรดสังเกตว่ามันแตกต่างจากระบบอื่น ๆ ที่ไม่ใช่ Linux ที่มีไฟล์พิเศษเหล่านั้น)
การอ่านบางสิ่งจาก stdin หมายถึงการอ่านจากไฟล์ descriptor 0 (ซึ่งจะชี้ไปที่ที่อยู่ภายในไฟล์ที่อ้างอิงโดย/dev/stdin
)
แต่ใน$(</dev/stdin)
เชลล์ไม่ได้อ่านจาก stdin จะเปิดตัวอธิบายไฟล์ใหม่เพื่ออ่านไฟล์เดียวกับที่เปิดบน stdin (ดังนั้นการอ่านจากจุดเริ่มต้นของไฟล์ไม่ใช่ตำแหน่งที่ stdin ชี้ไปที่)
ยกเว้นในกรณีพิเศษของอุปกรณ์เทอร์มินัลที่เปิดในโหมดอ่าน + เขียนโดยปกติ stdout และ stderr จะไม่เปิดให้อ่าน พวกเขาจะหมายถึงการเป็นลำธารที่คุณเขียนถึง ดังนั้นการอ่านจาก file descriptor 1 โดยทั่วไปจะไม่ทำงาน บน Linux การเปิด/dev/stdout
หรือ/dev/stderr
การอ่าน (ตาม$(</dev/stdout)
) จะทำงานและจะให้คุณอ่านจากไฟล์ที่ stdout ไปที่ (และถ้า stdout เป็นไพพ์จะอ่านจากปลายอีกด้านหนึ่งของท่อและถ้าเป็นซ็อกเก็ต มันจะล้มเหลวเนื่องจากคุณไม่สามารถเปิดซ็อกเก็ตได้)
ในกรณีที่สคริปต์ของเรารันโดยไม่มีการเปลี่ยนเส้นทางที่พร้อมต์ของเชลล์แบบโต้ตอบในเทอร์มินัลทั้งหมด / dev / stdin, / dev / stdout และ / dev / stderr จะเป็นไฟล์อุปกรณ์เทอร์มินัล / dev / pts / x
การอ่านจากไฟล์พิเศษเหล่านั้นจะส่งคืนสิ่งที่เทอร์มินัลส่ง (สิ่งที่คุณพิมพ์บนคีย์บอร์ด) การเขียนถึงพวกเขาจะส่งข้อความไปยังเครื่อง (เพื่อแสดง)
echo $(</dev/stdin)
echo $(</dev/stderr)
จะเหมือนกัน เพื่อขยาย$(</dev/stdin)
เชลล์จะเปิด / dev / pts / 0 และอ่านสิ่งที่คุณพิมพ์จนกว่าคุณจะกด^D
บนบรรทัดว่าง พวกเขาจะผ่านการขยายตัว (สิ่งที่คุณพิมพ์ตัดขึ้นบรรทัดใหม่และขึ้นอยู่กับการแบ่ง + glob) echo
ซึ่งจะส่งออกใน stdout (สำหรับการแสดงผล)
อย่างไรก็ตามใน:
echo $(</dev/stdout)
ในbash
( และbash
เท่านั้น ) สิ่งสำคัญคือต้องตระหนักว่าภายใน$(...)
stdout ได้ถูกเปลี่ยนเส้นทาง ตอนนี้มันเป็นท่อ ในกรณีของbash
กระบวนการเปลือกลูกคือการอ่านเนื้อหาของไฟล์ (ที่นี่/dev/stdout
) และเขียนมันไปยังท่อในขณะที่ผู้ปกครองอ่านจากปลายอีกด้านเพื่อทำการขยายตัว
ในกรณีนี้เมื่อกระบวนการทุบตีเด็กนั้นเปิดขึ้น/dev/stdout
จริง ๆ แล้วเป็นการเปิดจุดสิ้นสุดการอ่านของไพพ์ ไม่มีอะไรจะมาจากที่นี่มันเป็นสถานการณ์การหยุดชะงัก
หากคุณต้องการอ่านจากไฟล์ stdout ที่สคริปต์ชี้ไปคุณควรหลีกเลี่ยง:
{ echo content of file on stdout: "$(</dev/fd/3)"; } 3<&1
นั่นจะเป็นการจำลอง fd 1 ไปยัง fd 3 ดังนั้น / dev / fd / 3 จะชี้ไปที่ไฟล์เดียวกันกับ / dev / stdout
ด้วยสคริปต์เช่น:
#! /bin/bash -
printf 'content of file on stdin: %s\n' "$(</dev/stdin)"
{ printf 'content of file on stdout: %s\n' "$(</dev/fd/3)"; } 3<&1
printf 'content of file on stderr: %s\n' "$(</dev/stderr)"
เมื่อทำงานเป็น:
echo bar > err
echo foo | myscript > out 2>> err
คุณจะเห็นในout
ภายหลัง:
content of file on stdin: foo
content of file on stdout: content of file on stdin: foo
content of file on stderr: bar
หากเมื่อเทียบกับการอ่านจาก/dev/stdin
, /dev/stdout
, /dev/stderr
คุณต้องการที่จะอ่านจาก stdin, stdout และ stderr (ซึ่งจะทำให้รู้สึกแม้แต่น้อย), คุณต้องการทำ:
#! /bin/sh -
printf 'what I read from stdin: %s\n' "$(cat)"
{ printf 'what I read from stdout: %s\n' "$(cat <&3)"; } 3<&1
printf 'what I read from stderr: %s\n' "$(cat <&2)"
หากคุณเริ่มต้นสคริปต์ที่สองอีกครั้งเป็น:
echo bar > err
echo foo | myscript > out 2>> err
คุณจะเห็นในout
:
what I read from stdin: foo
what I read from stdout:
what I read from stderr:
และในerr
:
bar
cat: -: Bad file descriptor
cat: -: Bad file descriptor
สำหรับ stdout และ stderr cat
ล้มเหลวเนื่องจากตัวให้คำอธิบายไฟล์เปิดสำหรับการเขียนเท่านั้นไม่ใช่การอ่านการขยาย$(cat <&3)
และ$(cat <&2)
ว่างเปล่า
ถ้าคุณเรียกมันว่า:
echo out > out
echo err > err
echo foo | myscript 1<> out 2<> err
(ที่<>
เปิดในโหมดอ่าน + เขียนโดยไม่มีการตัดทอน) คุณจะเห็นในout
:
what I read from stdin: foo
what I read from stdout:
what I read from stderr: err
และในerr
:
err
คุณจะสังเกตเห็นว่าไม่มีสิ่งใดถูกอ่านจาก stdout เพราะก่อนหน้านี้printf
ได้เขียนทับเนื้อหาของout
ด้วยwhat I read from stdin: foo\n
และปล่อยตำแหน่ง stdout ไว้ในไฟล์นั้นหลังจากนั้น หากคุณเตรียมout
ข้อความขนาดใหญ่ไว้แล้วเช่น:
echo 'This is longer than "what I read from stdin": foo' > out
จากนั้นคุณจะได้รับในout
:
what I read from stdin: foo
read from stdin": foo
what I read from stdout: read from stdin": foo
what I read from stderr: err
มาดูกันว่าผู้$(cat <&3)
อ่านได้อ่านสิ่งที่เหลืออยู่หลังจากครั้งแรกอย่างไรprintf
และย้ายตำแหน่ง stdout ที่ผ่านมาเพื่อให้printf
ได้รับสิ่งที่อ่านต่อไป
echo x
นั้นไม่เหมือนกับecho x > /dev/stdout
ว่า stdout ไม่ได้ไปที่ไพพ์หรืออุปกรณ์ตัวละครบางตัวเช่นอุปกรณ์ tty ตัวอย่างเช่นหาก stdout ไปที่ไฟล์ปกติecho x > /dev/stdout
จะตัดทอนไฟล์และแทนที่เนื้อหาด้วยx\n
แทนที่จะเขียนx\n
ที่ตำแหน่ง stdout ปัจจุบัน