3 สาย: stdin, stdout, stderr + redirection
เข้าใจว่าทุกคำสั่งมี 3 สาย input/output/error แยกกัน + redirect > >> 2> 2>&1 ส่งออกไปไฟล์หรือ /dev/null
4 บทแรกเห็น output ของคำสั่งเด้งขึ้นหน้าจอมาตลอด ไม่เคยคิดว่ามันมาจากไหน พอเริ่ม debug script ที่ AI เขียนจริงจังจะเจอคำถาม “output ของคำสั่งหายไปไหน” เก็บเป็นไฟล์ได้ไหม แยก error ออกจาก output ปกติได้ไหม ทำให้คำสั่งเงียบสนิทได้ไหม
คำตอบอยู่ที่ mental model สำคัญของ Unix ที่ว่า ทุกคำสั่งมี 3 สาย เปรียบเหมือนท่อ ที่ไหลเข้า-ออกจากคำสั่ง เข้าใจ 3 สายนี้แล้ว redirection กับ pipe (บทหน้า) จะเป็นเรื่องธรรมชาติทันที
3 สายของคำสั่ง
ทุกคำสั่งใน Unix รับ-ส่งข้อมูลผ่าน 3 สายหลัก แยกชัดไม่ปนกัน
- stdin (standard input, เลข 0) = สายเข้า ปกติมาจาก keyboard ที่พิมพ์ตอบคำสั่งที่ถาม
- stdout (standard output, เลข 1) = สายออกปกติ output ที่คำสั่งพ่นออกมา ปกติไหลไป terminal screen
- stderr (standard error, เลข 2) = สายออกของ error ปกติก็ไปที่ screen เหมือนกันแต่เป็นคนละสาย กับ stdout
ที่ต้องแยก stderr ออกจาก stdout เพราะบางครั้งอยากเก็บเฉพาะ output ปกติไว้เป็นไฟล์ แต่ยังอยากเห็น error บนหน้าจอ หรือสลับกันก็ได้ พอแยกสายตั้งแต่ระดับ OS เลยทำแบบนี้ได้ง่าย
Redirection: เปลี่ยนทางไหลของสาย
เครื่องหมาย redirection เป็นเครื่องมือบอก shell ว่า “ให้สายนี้ไหลไปที่ไหนแทนที่จะเป็น default”
ls > files.txtecho done >> log.txtnpm install 2> errors.txtcmd > all.log 2>&1ลองเห็นสายกันจริงๆ
Widget ข้างล่างจำลอง ls *.log *.bad ที่มี stdout (3 ไฟล์ที่หาเจอ) กับ stderr (1 error เพราะ *.bad ไม่มีจริง) เลือก preset ดูว่าใส่ redirection แบบต่างๆ แล้ว แต่ละสายไปไหน
/dev/null: หลุมดำของระบบ
/dev/null เป็นไฟล์พิเศษที่ทำหน้าที่เป็น “หลุมดำ” อะไรส่งเข้าไปก็หายทันที ไม่ได้เก็บไว้ที่ไหน ใช้ตอนอยาก “รันแต่ไม่เห็น output”
$ noisy-script.sh > /dev/null # ไม่อยากเห็น stdout
$ noisy-script.sh 2> /dev/null # ไม่อยากเห็น stderr
$ noisy-script.sh > /dev/null 2>&1 # เงียบสนิทใช้บ่อยใน cron job (งานที่ตั้งเวลารันอัตโนมัติ) หรือ background service ที่ไม่อยากให้ output ไป log ระบบ
stdin < file: อ่านจากไฟล์แทน keyboard
ใช้น้อยกว่าตัวอื่น แต่ก็เจอ เวลาต้องการให้คำสั่งอ่าน input จากไฟล์ (เช่นใส่ password, import SQL) แทนที่คุณจะพิมพ์เอง shell จะส่งเนื้อไฟล์เข้า stdin ให้เอง
$ psql mydb < schema.sql # ป้อนเนื้อ schema.sql ให้ psql
$ wc -l < README.md # นับบรรทัดจาก README.mdในชีวิตจริง < ใช้น้อยลงเพราะ pipe (|) ในบทหน้ายืดหยุ่นกว่า แต่ก็ควรจำหน้าตาไว้ จะได้อ่านออกเวลาเห็น
สรุป
- ทุกคำสั่งมี 3 สาย:
stdin(0),stdout(1),stderr(2) >ส่ง stdout ไปไฟล์ (ทับ),>>ต่อท้าย (ไม่ทับ)2>ส่ง stderr ไปไฟล์,2>&1รวม stderr ตาม stdout/dev/nullคือหลุมดำ เขียนลงไปหายไปเลย ใช้ทำให้คำสั่งเงียบ< fileอ่าน stdin จากไฟล์แทน keyboard
ตอนนี้รู้จัก 3 สายแล้ว บทต่อไปจะใช้สายนี้ทำ superpower ของ Unix คือ pipe (|) ที่ต่อ stdout ของคำสั่งหนึ่งเข้า stdin ของคำสั่งถัดไป สร้าง workflow จากคำสั่งเล็กๆ หลายตัวต่อกันเป็น pipeline ได้