錯誤記錄是很重要的
身為一個專業的工程師,當程式出現錯誤時,一定要把錯誤好好的記錄下來,而要能夠把錯誤有彈性的記錄下來,在Java上請使用log4j,在Python上請好好研究logging module, 在C++上有log4j的兄弟: log4cxx。若不好好善用這些logging module, 你所寫出來的服務是絕對沒有辨法在Production環境下生存的。
那吐出 std error 的那些壞孩子呢?
每個人都有不得已的時候,總是有許多的程式是不得不把訊息送往std out, 及 std error. 比如說一些簡短 Shell Script 或是較老舊的程式碼。
那我們只好另外把這些訊息集中處理。
這樣的工作看似不難,工程師跟Shell 一定有點交情,IO Redirection 多少都有交手過幾次,直覺可能會寫出類似下面的指令:
some_command 2>&1 >/var/log/certain_message
字面上直接翻譯: "我想把std error 導到 std out, 再把 std out 導到檔案中"。
但是事情絕對沒有那簡單,上面的指令是不work的。
但是事情絕對沒有那簡單,上面的指令是不work的。
你可以用下面這個指令來認清事實:
python -c "from sys import *; stderr.write('err'); stdout.write('out')" 2>&1 > /tmp/o.txt
這個指令在沒有IO Redirect前,會送 'err' 這個字串到 std error ,送 'out' 到 std out。而在設定完IO Redirection後,err這串字會竟然還是只會出現在畫面上,而out 會出現在 /tmp/o.txt 這個檔案裡。
唉,事與願違,錢不好賺。
唉,事與願違,錢不好賺。
我該怎麼做
如果你是個懶鬼,我就跟你說,換順序就好,本篇文章可以不用看下去了,但記得給個讚之類的東西。
some_command >/var/log/certain_message 2>&1
上面這段 Command 就可以把所有的訊息收到集起來了,把 2>&1 往後擺即可。
但要能夠理解順序的影響,就要記得一個Process 與其所掌握IO Device 之間隔了一層抽象層: File Descriptor. Process 是經由 File Descriptor 才能對 IO Device 有所影響。
我們先以這個簡短的command 來做圖解釋: echo "cnt" 2>&1 > o.txt 。
並且請記得,預設情況下,FD1是指向 std out, FD2是指向 std error
下圖一是我們還沒有做任何 IO Redirection 以前的樣貌。
Bash 的man page 有說到,會由左到右分析指令。
當以在圖二中,Shell 會首先分析 2>&1 ,這會讓FD 2 指向 FD 1 所指向的裝置。因此,FD2會指向 std out. 而 std err 就像是個 dangling pointer,不再有機會被使用到了。
下圖三: 下一個IO Redirection: > o.txt, 使 FD1指向了 o.txt.
這只改變FD1, 不會對 FD 2 造成任何影響, FD2 還是指向 std out。
這樣子的結果,達不到我們想要收集訊息的目地。
那如下圖四的正確順序指令 echo "cnt" > o.txt 2>&1 又會呈現什麼樣貌呢?
Shell 首先面對了 > o.txt, 會把 FD1 指向 o.txt.
在下圖六,Shell 遇到了 2>&1, 會把FD2 指向 FD1 所指向的IO Device, 也就是 o.txt。
但要能夠理解順序的影響,就要記得一個Process 與其所掌握IO Device 之間隔了一層抽象層: File Descriptor. Process 是經由 File Descriptor 才能對 IO Device 有所影響。
我們先以這個簡短的command 來做圖解釋: echo "cnt" 2>&1 > o.txt 。
並且請記得,預設情況下,FD1是指向 std out, FD2是指向 std error
下圖一是我們還沒有做任何 IO Redirection 以前的樣貌。
圖一: File Descriptor 與 IO Device 的對應 |
Bash 的man page 有說到,會由左到右分析指令。
當以在圖二中,Shell 會首先分析 2>&1 ,這會讓FD 2 指向 FD 1 所指向的裝置。因此,FD2會指向 std out. 而 std err 就像是個 dangling pointer,不再有機會被使用到了。
圖二: 2 指向了 std out, 而此時, std error 成了孤兒了 |
下圖三: 下一個IO Redirection: > o.txt, 使 FD1指向了 o.txt.
這只改變FD1, 不會對 FD 2 造成任何影響, FD2 還是指向 std out。
這樣子的結果,達不到我們想要收集訊息的目地。
圖三: IO Redirection 設定結束了,只有FD1 被收集到了檔案中 |
那如下圖四的正確順序指令 echo "cnt" > o.txt 2>&1 又會呈現什麼樣貌呢?
圖四: 正確順序的指令又會呈現什麼樣貌呢? |
圖五: FD1 被指向了 o.txt |
在下圖六,Shell 遇到了 2>&1, 會把FD2 指向 FD1 所指向的IO Device, 也就是 o.txt。
最後,我們的Process 對FD1及FD2 的output, 都會直接導向了 o.txt.
結論
上面這些圖,是幫助我們理解Shell 背後的運作,只要記住 file descriptor 那一層間接性,我們就可以清楚地推導出 io redirection 所造成的結果而不用去硬記指令的寫法,如果你想寫出更複雜的指令組合,也絕不會出錯。 如果喜歡這類文章,你在這個Blog可以看到加入 icoding 粉絲團的方式,請不吝惜給我們支持與鼓勵!
some_command &> /var/log/certain_message
回覆刪除這樣也可以
Thanks artis,
回覆刪除Bash 的man page 也有講到. 除了明確指定file descriptor,
&>word 跟
>word 2>&1
是等義的,所以是Bash是有更簡單的做法的。