2012年7月9日 星期一

到底怎麼樣可以同時把std error, std out 記錄到檔案中呢?

錯誤記錄是很重要的

身為一個專業的工程師,當程式出現錯誤時,一定要把錯誤好好的記錄下來,而要能夠把錯誤有彈性的記錄下來,在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的。

你可以用下面這個指令來認清事實:

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 以前的樣貌。

圖一: 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 又會呈現什麼樣貌呢?

圖四: 正確順序的指令又會呈現什麼樣貌呢?
 Shell 首先面對了 > o.txt, 會把 FD1 指向 o.txt.
圖五: 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 粉絲團的方式,請不吝惜給我們支持與鼓勵!


  

2 則留言:

  1. some_command &> /var/log/certain_message

    這樣也可以

    回覆刪除
  2. Thanks artis,

    Bash 的man page 也有講到. 除了明確指定file descriptor,

    &>word 跟

    >word 2>&1

    是等義的,所以是Bash是有更簡單的做法的。

    回覆刪除

Related Posts Plugin for WordPress, Blogger...