標準串流(stdin、stdout、stderr)(standard streams)
想像每個程式都是工作檯上的一台小機器,接著三條管子:一條把材料送進去、一條把成品送出來、第三條細管子專門給警示燈與錯誤訊息用。關鍵是:這台機器不在乎輸入管接的是你的鍵盤、還是接到另一台機器的軟管——它只管讀進流進來的東西。這三條管子就是每個 Unix 程式起步時就有的標準串流。
具體來說,當一個行程啟動時,依慣例會被交給三個已經開啟的連線,以稱為檔案描述符的小整數來辨識。標準輸入(stdin)是檔案描述符 0——程式從這裡讀取輸入。標準輸出(stdout)是檔案描述符 1——程式把正常結果寫到這裡。標準錯誤(stderr)是檔案描述符 2——程式把診斷與錯誤訊息寫到這裡,刻意分開,好讓錯誤文字不會混進並汙染真正的輸出。預設情況下 0、1、2 都連到你的終端機,但殼層可以把它們任何一個重導向到檔案或另一個程式,而程式並不知道、也不在乎:這正是 ./prog < in.txt > out.txt 以及像 a | b 這樣的管線之所以能運作的原因。C 函式庫把這些描述符包裝成方便的串流物件 stdin、stdout、stderr,供你搭配 printf() 等函式使用。
為何重要:這個小小的慣例——三條編號的串流、輸出與錯誤分開——正是讓 Unix 程式可組合的原因。因為一個程式從描述符 0 讀、往描述符 1 寫、而不在乎另一端是什麼,殼層就能把一個程式的輸出接到下一個程式的輸入,用許多小工具搭出強大的管線。把真正的輸出送到 stdout、把錯誤送到 stderr 的紀律,正是讓你能把結果擷取到檔案、同時仍在螢幕上看到錯誤訊息的關鍵。
執行 ./prog > out.txt,殼層會把描述符 1(stdout)接到那個檔案,所以正常輸出落進 out.txt——但程式寫到 stderr(描述符 2)的任何東西仍會出現在你的終端機上,因為你只重導向了 1、沒有重導向 2。
輸出與錯誤走在不同的描述符上,所以它們可以被分開導向。
把結果送到 stdout(1)、把診斷送到 stderr(2),不要反過來。把錯誤文字混進 stdout,會汙染某人可能正導入另一個程式的資料;這正是為什麼 printf() 送往 stdout,而錯誤回報應該送往 stderr。