echo หรือ print / dev / stdin / dev / stdout / dev / stderr


14

ฉันต้องการพิมพ์ค่าของ / dev / stdin, / dev / stdout และ / dev / stderr

นี่คือสคริปต์ง่าย ๆ ของฉัน:

#!/bin/bash
echo your stdin is : $(</dev/stdin)
echo your stdout is : $(</dev/stdout)
echo your stderr is : $(</dev/stderr)

ฉันใช้ท่อต่อไปนี้:

[root@localhost home]# ls | ./myscript.sh
[root@localhost home]# testerr | ./myscript.sh

เพียง แต่$(</dev/stdin)ดูเหมือนว่าจะทำงานผมยังได้พบกับคนอื่น ๆ บางคนที่ใช้คำถาม: "${1-/dev/stdin}"พยายามมันไม่ประสบความสำเร็จ

คำตอบ:


30

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ได้รับสิ่งที่อ่านต่อไป


2

stdoutและstderrเป็นเอาท์พุทคุณไม่ได้อ่านจากพวกเขาคุณสามารถเขียนได้เท่านั้น ตัวอย่างเช่น:

echo "this is stdout" >/dev/stdout
echo "this is stderr" >/dev/stderr

โปรแกรมเขียนไปยัง stdout โดยปริยายดังนั้นโปรแกรมแรกจะเทียบเท่ากับ

echo "this is stdout"

และคุณสามารถเปลี่ยนเส้นทาง stderr ด้วยวิธีอื่นเช่น

echo "this is stderr" 1>&2

1
บน Linux echo xนั้นไม่เหมือนกับecho x > /dev/stdoutว่า stdout ไม่ได้ไปที่ไพพ์หรืออุปกรณ์ตัวละครบางตัวเช่นอุปกรณ์ tty ตัวอย่างเช่นหาก stdout ไปที่ไฟล์ปกติecho x > /dev/stdoutจะตัดทอนไฟล์และแทนที่เนื้อหาด้วยx\nแทนที่จะเขียนx\nที่ตำแหน่ง stdout ปัจจุบัน
Stéphane Chazelas

@ Michael-Daffin> ขอบคุณดังนั้นหากฉันต้องการอ่าน stdout และ stderr ฉันสามารถใช้ cat ได้หรือไม่ (เนื่องจากเป็นไฟล์) ตัวอย่างเช่นฉันต้องการให้ผู้ใช้เห็นข้อผิดพลาดล่าสุดที่เกิดขึ้นecho this is your error $(cat /dev/stderr)?
Omar BISTAMI

@OmarBISTAMI คุณไม่สามารถอ่านจากstdoutและstderrเป็นผลลัพธ์
Kusalananda

1

myscript.sh:

#!/bin/bash
filan -s

จากนั้นเรียกใช้:

$ ./myscript.sh
    0 tty /dev/pts/1
    1 tty /dev/pts/1
    2 tty /dev/pts/1

$ ls | ./myscript.sh
    0 pipe 
    1 tty /dev/pts/1
    2 tty /dev/pts/1

$ ls | ./myscript.sh > out.txt
$ cat out.txt
    0 pipe 
    1 file /tmp/out.txt
    2 tty /dev/pts/1

คุณอาจจะต้องติดตั้งfilanกับsudo apt install socatครั้งแรก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.