stdin, stdout และ stderr บน Linux คืออะไร

เผยแพร่แล้ว: 2022-01-29
หน้าต่างเทอร์มินัลบนคอมพิวเตอร์ Linux
Fatmawati Achmad Zaenuri/Shutterstock.com

stdin , stdout และ stderr เป็นสตรีมข้อมูลสามสตรีมที่สร้างขึ้นเมื่อคุณเรียกใช้คำสั่ง Linux คุณสามารถใช้เพื่อบอกว่าสคริปต์ของคุณถูกวางหรือเปลี่ยนเส้นทางหรือไม่ เราแสดงให้คุณเห็นว่า

สตรีมเข้าร่วมสองคะแนน

ทันทีที่คุณเริ่มเรียนรู้เกี่ยวกับระบบปฏิบัติการที่คล้ายกับ Linux และ Unix คุณจะเจอเงื่อนไข stdin , stdout และ stederr นี่คือสตรีมมาตรฐานสามสตรีมที่สร้างขึ้นเมื่อรันคำสั่ง Linux ในการคำนวณ สตรีมคือสิ่งที่สามารถถ่ายโอนข้อมูลได้ ในกรณีของสตรีมเหล่านี้ ข้อมูลนั้นเป็นข้อความ

กระแสข้อมูล เช่น ธารน้ำ มีสองปลาย พวกเขามีแหล่งที่มาและการไหลออก คำสั่ง Linux ใดก็ตามที่คุณใช้จะมีปลายด้านหนึ่งของแต่ละสตรีม ปลายอีกด้านหนึ่งถูกกำหนดโดยเชลล์ที่เรียกใช้คำสั่ง ปลายทางนั้นจะเชื่อมต่อกับหน้าต่างเทอร์มินัล เชื่อมต่อกับไพพ์ หรือเปลี่ยนเส้นทางไปยังไฟล์หรือคำสั่งอื่นๆ ตามบรรทัดคำสั่งที่เรียกใช้คำสั่ง

สตรีมมาตรฐานของลินุกซ์

ใน Linux stdin เป็นสตรีมอินพุตมาตรฐาน นี้ยอมรับข้อความเป็นข้อมูลเข้า เอาต์พุตข้อความจากคำสั่งไปยังเชลล์ถูกส่งผ่านสตรีม stdout (เอาต์พุตมาตรฐาน) ข้อความแสดงข้อผิดพลาดจากคำสั่งจะถูกส่งผ่านสตรีม stderr (ข้อผิดพลาดมาตรฐาน)

ดังนั้นคุณจะเห็นว่ามีสองเอาต์พุตสตรีม stdout และ stderr และหนึ่งอินพุตสตรีม stdin เนื่องจากข้อความแสดงข้อผิดพลาดและเอาต์พุตปกติแต่ละรายการมีท่อร้อยสายเพื่อนำไปยังหน้าต่างเทอร์มินัล จึงสามารถจัดการแยกจากกันได้

สตรีมได้รับการจัดการเหมือนไฟล์

สตรีมใน Linux—เหมือนกับเกือบทุกอย่าง—จะถือว่ามันเป็นไฟล์ คุณสามารถอ่านข้อความจากไฟล์ และเขียนข้อความลงในไฟล์ได้ การกระทำทั้งสองนี้เกี่ยวข้องกับกระแสข้อมูล ดังนั้นแนวคิดในการจัดการกระแสข้อมูลในฐานะไฟล์จึงไม่ได้ยืดเยื้อมากนัก

โฆษณา

แต่ละไฟล์ที่เกี่ยวข้องกับกระบวนการจะได้รับการจัดสรรหมายเลขเฉพาะเพื่อระบุ สิ่งนี้เรียกว่าตัวอธิบายไฟล์ เมื่อใดก็ตามที่จำเป็นต้องดำเนินการกับไฟล์ ตัวอธิบายไฟล์จะถูกใช้เพื่อระบุไฟล์

ค่าเหล่านี้มักใช้สำหรับ stdin , stdout, และ stderr :

  • 0 : stdin
  • 1 : stdout
  • 2 : stderr

การตอบสนองต่อท่อและการเปลี่ยนเส้นทาง

เพื่อให้การแนะนำของผู้อื่นง่ายขึ้น เทคนิคทั่วไปคือการสอนหัวข้อในรูปแบบที่เรียบง่าย ตัวอย่างเช่น ด้วยไวยากรณ์ เราได้รับแจ้งว่ากฎคือ "ฉันก่อน E ยกเว้นหลัง C" แต่จริงๆ แล้ว มีข้อยกเว้นสำหรับกฎนี้มากกว่าที่มีกรณีที่ปฏิบัติตาม

ในทำนองเดียวกัน เมื่อพูดถึง stdin , stdout , และ stderr เป็นการสะดวกที่จะแยกแยะสัจพจน์ที่ยอมรับได้ว่ากระบวนการไม่รู้และไม่สนใจว่าสตรีมมาตรฐานทั้งสามจะสิ้นสุดลงที่ใด กระบวนการควรดูแลว่าผลลัพธ์จะถูกส่งไปยังเทอร์มินัลหรือถูกเปลี่ยนเส้นทางไปยังไฟล์หรือไม่? มันสามารถบอกได้หรือไม่ว่าอินพุตนั้นมาจากคีย์บอร์ดหรือถูกส่งมาจากกระบวนการอื่น?

อันที่จริง กระบวนการรู้ดี—หรืออย่างน้อยก็หาได้ ควรจะเลือกตรวจสอบ—และมันสามารถเปลี่ยนพฤติกรรมของมันได้ตามนั้น ถ้าผู้สร้างซอฟต์แวร์ตัดสินใจเพิ่มฟังก์ชันนั้น

โฆษณา

เราสามารถเห็นการเปลี่ยนแปลงพฤติกรรมนี้ได้อย่างง่ายดายมาก ลองสองคำสั่งนี้:

 ลส 

 ls | แมว 

คำสั่ง ls จะทำงานแตกต่างกันหากเอาต์พุต ( stdout ) ถูกไพพ์ไปยังคำสั่งอื่น มันคือ ls ที่สลับเป็นเอาต์พุตคอลัมน์เดียว ไม่ใช่การแปลงที่ดำเนินการโดย cat และ ls จะทำสิ่งเดียวกันหากผลลัพธ์ถูกเปลี่ยนเส้นทาง:

 ls > capture.txt 

 cat capture.txt 

กำลังเปลี่ยนเส้นทาง stdout และ stderr

การมีข้อความแสดงข้อผิดพลาดที่ส่งโดยสตรีมเฉพาะนั้นมีประโยชน์ หมายความว่าเราสามารถเปลี่ยนเส้นทางเอาต์พุตของคำสั่ง ( stdout ) ไปยังไฟล์และยังคงเห็นข้อความแสดงข้อผิดพลาด ( stderr ) ในหน้าต่างเทอร์มินัล คุณสามารถตอบสนองต่อข้อผิดพลาดได้หากต้องการ เมื่อมันเกิดขึ้น นอกจากนี้ยังหยุดข้อความแสดงข้อผิดพลาดจากการปนเปื้อนไฟล์ที่ stdout ถูกเปลี่ยนเส้นทางเข้าไป

พิมพ์ข้อความต่อไปนี้ในเอดิเตอร์และบันทึกลงในไฟล์ชื่อ error.sh

 #!/bin/bash

echo "กำลังพยายามเข้าถึงไฟล์ที่ไม่มีอยู่"
cat bad-filename.txt

ทำให้สคริปต์ทำงานได้ด้วยคำสั่งนี้:

 chmod +x error.sh

บรรทัดแรกของสคริปต์สะท้อนข้อความไปยังหน้าต่างเทอร์มินัล ผ่านสตรีม stdout บรรทัดที่สองพยายามเข้าถึงไฟล์ที่ไม่มีอยู่ สิ่งนี้จะสร้างข้อความแสดงข้อผิดพลาดที่ส่งผ่าน stderr

รันสคริปต์ด้วยคำสั่งนี้:

 ./error.sh 

เราจะเห็นได้ว่าทั้งสตรีมของเอาต์พุต stdout และ stderr ถูกแสดงในหน้าต่างเทอร์มินัล

ลองเปลี่ยนเส้นทางผลลัพธ์ไปยังไฟล์:

 ./error.sh > capture.txt 

โฆษณา

ข้อความแสดงข้อผิดพลาดที่ส่งผ่าน stderr ยังคงถูกส่งไปยังหน้าต่างเทอร์มินัล เราสามารถตรวจสอบเนื้อหาของไฟล์เพื่อดูว่าเอาท์พุต stdout ไปที่ไฟล์หรือไม่

 cat capture.txt 

เอาต์พุตจาก stdin ถูกเปลี่ยนเส้นทางไปยังไฟล์ตามที่คาดไว้

สัญลักษณ์การเปลี่ยนเส้นทาง > ใช้งานได้กับ stdout โดยค่าเริ่มต้น คุณสามารถใช้ตัวอธิบายไฟล์ตัวเลขตัวใดตัวหนึ่งเพื่อระบุว่าสตรีมเอาต์พุตมาตรฐานใดที่คุณต้องการเปลี่ยนเส้นทาง

หากต้องการเปลี่ยนเส้นทาง stdout อย่างชัดเจน ให้ใช้คำแนะนำการเปลี่ยนเส้นทางนี้:

 1>

หากต้องการเปลี่ยนเส้นทาง stderr อย่างชัดเจน ให้ใช้คำแนะนำการเปลี่ยนเส้นทางนี้:

 2>

มาลองทดสอบกันอีกครั้ง คราวนี้เราจะใช้ 2> :

 ./error.sh 2> capture.txt 

ข้อความแสดงข้อผิดพลาดถูกเปลี่ยนเส้นทางและข้อความ echo stdout ถูกส่งไปยังหน้าต่างเทอร์มินัล:

มาดูกันว่าในไฟล์ capture.txt มีอะไรบ้าง

 cat capture.txt 

ข้อความ stderr อยู่ใน capture.txt ตามที่คาดไว้

การเปลี่ยนเส้นทางทั้ง stdout และ stderr

แน่นอน หากเราเปลี่ยนเส้นทาง stdout หรือ stderr ไปยังไฟล์โดยไม่แยกจากกัน เราควรจะเปลี่ยนเส้นทางทั้งสองไฟล์พร้อมกันเป็นสองไฟล์ได้หรือไม่

โฆษณา

ใช่เราทำได้ คำสั่งนี้จะนำ stdout ไปยังไฟล์ชื่อ capture.txt และ stderr ไปยังไฟล์ชื่อ error.txt

 ./error.sh 1> capture.txt 2> error.txt 

เนื่องจากทั้งสตรีมของเอาต์พุต - เอาต์พุตมาตรฐานและข้อผิดพลาดมาตรฐาน - ถูกเปลี่ยนเส้นทางไปยังไฟล์ จึงไม่มีเอาต์พุตที่มองเห็นได้ในหน้าต่างเทอร์มินัล เราจะกลับไปที่พรอมต์บรรทัดคำสั่งราวกับว่าไม่มีอะไรเกิดขึ้น

มาตรวจสอบเนื้อหาของแต่ละไฟล์กัน:

 cat capture.txt
 cat error.txt 

กำลังเปลี่ยนเส้นทาง stdout และ stderr ไปยังไฟล์เดียวกัน

เรียบร้อย เรามีเอาต์พุตสตรีมมาตรฐานไปยังไฟล์เฉพาะของตัวเอง ชุดค่าผสมอื่นที่เราสามารถทำได้คือส่งทั้ง stdout และ stderr ไปยังไฟล์เดียวกัน

เราสามารถทำได้ด้วยคำสั่งต่อไปนี้:

 ./error.sh > capture.txt 2>&1

มาทำลายมันกันเถอะ

  • ./error.sh : เปิดไฟล์สคริปต์ error.sh
  • > capture.txt : เปลี่ยนเส้นทางสตรีม stdout ไปยังไฟล์ capture.txt > เป็นชวเลขสำหรับ 1>
  • 2>&1 : ใช้คำสั่งเปลี่ยนเส้นทาง &> คำแนะนำนี้ช่วยให้คุณบอกเชลล์เพื่อให้สตรีมหนึ่งไปยังปลายทางเดียวกันกับสตรีมอื่น ในกรณีนี้ เรากำลังพูดว่า “redirect stream 2, stderr , ไปยังปลายทางเดียวกันกับที่ stream 1, stdout ถูกเปลี่ยนเส้นทางไป”

ไม่มีผลลัพธ์ที่มองเห็นได้ นั่นเป็นกำลังใจ

ลองตรวจสอบไฟล์ capture.txt และดูว่ามีอะไรอยู่ในนั้น

 cat capture.txt 

ทั้งสตรีม stdout และ stderr ถูกเปลี่ยนเส้นทางไปยังไฟล์ปลายทางเดียว

หากต้องการให้เอาต์พุตของสตรีมถูกเปลี่ยนเส้นทางและถูกโยนทิ้งไปอย่างเงียบๆ ให้นำเอาต์พุตไปที่ /dev/null

การตรวจจับการเปลี่ยนเส้นทางภายในสคริปต์

เราได้พูดคุยกันถึงวิธีที่คำสั่งสามารถตรวจจับได้ว่าสตรีมใดถูกเปลี่ยนเส้นทางหรือไม่ และสามารถเลือกที่จะปรับเปลี่ยนลักษณะการทำงานตามนั้นได้ เราสามารถทำสิ่งนี้ให้สำเร็จในสคริปต์ของเราเองได้หรือไม่? ใช่เราทำได้ และเป็นเทคนิคที่ง่ายมากที่จะเข้าใจและนำไปใช้

โฆษณา

พิมพ์ข้อความต่อไปนี้ในเอดิเตอร์และบันทึกเป็น input.sh

 #!/bin/bash

ถ้า [ -t 0]; แล้ว

  echo stdin มาจากแป้นพิมพ์
 
อื่น

  echo stdin มาจากไพพ์หรือไฟล์
 
fi

ใช้คำสั่งต่อไปนี้เพื่อให้ปฏิบัติการได้:

 chmod +x อินพุต.sh

ส่วนที่ฉลาดคือการทดสอบภายในวงเล็บเหลี่ยม อ็อพชัน -t (เทอร์มินัล) คืนค่า true (0) หากไฟล์ที่เกี่ยวข้องกับ file descriptor สิ้นสุดลงในหน้าต่างเทอร์มินัล เราใช้ file descriptor 0 เป็นอาร์กิวเมนต์ในการทดสอบ ซึ่งแสดงถึง stdin

หาก stdin เชื่อมต่อกับหน้าต่างเทอร์มินัล การทดสอบจะพิสูจน์ได้ว่าเป็นจริง หาก stdin เชื่อมต่อกับไฟล์หรือไพพ์ การทดสอบจะล้มเหลว

เราสามารถใช้ไฟล์ข้อความที่สะดวกเพื่อสร้างอินพุตให้กับสคริปต์ได้ ที่นี่เราใช้อันหนึ่งเรียกว่า dummy.txt

 ./input.sh < dummy.txt 

ผลลัพธ์แสดงว่าสคริปต์รับรู้ว่าอินพุตไม่ได้มาจากแป้นพิมพ์ แต่มาจากไฟล์ หากคุณเลือก คุณสามารถเปลี่ยนพฤติกรรมของสคริปต์ได้ตามความเหมาะสม

โฆษณา

นั่นคือการเปลี่ยนเส้นทางไฟล์มาลองใช้กับไพพ์

 cat dummy.txt | ./input.sh 

สคริปต์รับรู้ว่ากำลังส่งอินพุตเข้าไป หรือแม่นยำกว่านั้น มันรับรู้อีกครั้งว่าสตรีม stdin ไม่ได้เชื่อมต่อกับหน้าต่างเทอร์มินัล

มารันสคริปต์กันโดยไม่มีไพพ์หรือเปลี่ยนเส้นทาง

 ./input.sh 

สตรีม stdin เชื่อมต่อกับหน้าต่างเทอร์มินัล และสคริปต์จะรายงานสิ่งนี้ตามนั้น

ในการตรวจสอบสิ่งเดียวกันกับเอาต์พุตสตรีม เราจำเป็นต้องมีสคริปต์ใหม่ พิมพ์ข้อมูลต่อไปนี้ในเอดิเตอร์และบันทึกเป็น output.sh

 #!/bin/bash

ถ้า [ -t 1 ]; แล้ว

echo stdout กำลังไปที่หน้าต่างเทอร์มินัล
 
อื่น

echo stdout กำลังถูกเปลี่ยนเส้นทางหรือวางท่อ
 
fi

ใช้คำสั่งต่อไปนี้เพื่อให้ปฏิบัติการได้:

 chmod +x อินพุต.sh

การเปลี่ยนแปลงที่สำคัญเพียงอย่างเดียวของสคริปต์นี้คือการทดสอบในวงเล็บเหลี่ยม เรากำลังใช้ตัวเลข 1 เพื่อแสดง file descriptor สำหรับ stdout

มาลองดูกัน เราจะไพพ์เอาต์พุตผ่าน cat

 ./output | แมว 

โฆษณา

สคริปต์รับรู้ว่าเอาต์พุตไม่ส่งไปยังหน้าต่างเทอร์มินัลโดยตรง

เรายังทดสอบสคริปต์ได้โดยเปลี่ยนเส้นทางเอาต์พุตไปยังไฟล์

 ./output.sh > capture.txt 

ไม่มีเอาต์พุตไปยังหน้าต่างเทอร์มินัล เราจะกลับไปที่พรอมต์คำสั่งอย่างเงียบ ๆ อย่างที่เราคาดหวัง

เราสามารถมองเข้าไปในไฟล์ capture.txt เพื่อดูว่ามีอะไรถูกดักไว้บ้าง ใช้คำสั่งต่อไปนี้เพื่อทำเช่นนั้น

 แมวจับ.sh 

อีกครั้ง การทดสอบอย่างง่ายในสคริปต์ของเราตรวจพบว่าสตรีม stdout ไม่ได้ส่งไปยังหน้าต่างเทอร์มินัลโดยตรง

โฆษณา

หากเราเรียกใช้สคริปต์โดยไม่มีไพพ์หรือการเปลี่ยนเส้นทาง สคริปต์ควรตรวจพบว่า stdout ถูกส่งโดยตรงไปยังหน้าต่างเทอร์มินัล

 ./output.sh 

และนั่นคือสิ่งที่เราเห็น

สายธารแห่งสติ

การรู้วิธีบอกว่าสคริปต์ของคุณเชื่อมต่อกับหน้าต่างเทอร์มินัล หรือไปป์ หรือกำลังถูกเปลี่ยนเส้นทาง ช่วยให้คุณปรับพฤติกรรมตามนั้นได้

การบันทึกและเอาต์พุตการวินิจฉัยอาจมีรายละเอียดมากหรือน้อย ขึ้นอยู่กับว่าจะไปที่หน้าจอหรือไปที่ไฟล์ ข้อความแสดงข้อผิดพลาดสามารถบันทึกลงในไฟล์อื่นที่ไม่ใช่เอาต์พุตของโปรแกรมปกติได้

ตามปกติแล้ว ความรู้ที่มากขึ้นทำให้เกิดทางเลือกมากขึ้น

ที่เกี่ยวข้อง: แล็ปท็อป Linux ที่ดีที่สุดสำหรับนักพัฒนาและผู้ที่ชื่นชอบ