探討shell命令中>/dev/null 2>&1的實現原理

來源:互聯網
上載者:User


探討shell命令中>/dev/null 2>&1的實現原理 首先標準輸入,標準輸出,標準錯誤:標準輸入是程式可以讀取其輸入的位置。預設情況下,進程從鍵盤讀取 stdin 。標準輸出是程式寫入其輸出的位置。預設情況下,進程將 stdout 寫到終端螢幕上。標準錯誤是程式寫入其錯誤訊息的位置。預設情況下,進程將 stderr 寫到終端螢幕上。
 為什麼有這三個很重要的概念呢?我們知道,一個程式要運行,需要有輸入、輸出,如果出錯,還要能表現出自身的錯誤。這是就要從某個地方讀入資料、將資料輸出到某個地方,出錯了還要把錯誤給弄到一個地方去.這就夠成了資料流(stream)。所以通常情況,每個 Unix 程式在啟動時都會開啟三個流,一個用於輸入,一個用於輸出,一個用於列印診斷或錯誤訊息。  www.2cto.com  有了這三個概念. 再說說重新導向: 資料流重導向(重新導向)就是將某個指令(命令)執行後的執行傳回值,一般這些傳回值就是你執行完後出現在螢幕上那些結果資料,如果我不想讓他預設流向螢幕.那麼我可以把這些結果資料轉送到其他的地方,例如檔案或者裝置(例如印表機,不過在Linux裡面一切都一切都是檔案,所以印表機這樣的裝置也是檔案咯).這樣資料就跑被我導向其他地方了.你懂的.所以東西都輸出到螢幕,如果資料太多太亂.我們也受不了啊.而且螢幕的terminal一關.東西就再也找不到了.如果我重新導向到一個檔案.這樣就可以長期儲存執行的日誌了. >資料流重導向:輸出導向,會替換被導向的檔案內容.>>資料流重導向:輸出導向,不會替換被導向的檔案內容.會在屁股後面累加資料.繼續看看檔案描述符:  www.2cto.com  維基百科,自由的百科全書上面是這樣說的,檔案描述符(File descriptor)是電腦科學中的一個術語,是一個用於表述指向檔案的引用的抽象化概念。檔案描述符在形式上是一個非負整數。實際上,它是一個索引值,指向核心為每一個進程所維護的該進程開啟檔案的記錄表。當程式開啟一個現有檔案或者建立一個新檔案時,核心向進程返回一個檔案描述符。在程式設計中,一些涉及底層的程式編寫往往會圍繞著檔案描述符展開。但是檔案描述符這一概念往往只適用於UNIX、Linux這樣的作業系統。

檔案描述符的優點主要有兩個:基於檔案描述符的I/O操作相容POSIX標準。在UNIX、Linux的系統調用中,大量的系統調用都是依賴於檔案描述符。看來這東西還真的有點抽象.也就是說如果程式不開啟,檔案孤單的在磁碟上面的時候是沒有檔案描述符的.可以想象一下.第一個開啟的檔案是0,第二個是1,依此類推。Unix 作業系統通常給每個進程能開啟的檔案數量強加一個限制。更甚的是,unix 通常有一個系統級的限制。當然真是的情況是0,1,2一般已經被某些概念佔用.再加上系統啟動後已經不知道開啟了多少檔案.

所以.我們自己一般開啟檔案的時候描述符估計也已經到很大的資料了.但是這檔案描述符缺點也是有的.比如完成的代碼可讀性也就會變得很差.你想啊.0,1,2....22231是知道是啥玩意兒?還好POSIX 定義了 STDIN_FILENO、STDOUT_FILENO 和 STDERR_FILENO 來代替 0、1、2。這三個符號常量的定義位於標頭檔 unistd.h。檔案描述符的有效範圍是 0 到 OPEN_MAX。一般來說,每個進程最多可以開啟 64 個檔案(0 — 63)。對於 FreeBSD 5.2.1、Mac OS X 10.3 和 Solaris 9 來說,每個進程最多可以開啟檔案的多少取決於系統記憶體的大小,int 的大小,以及系統管理員設定的限制。Linux 2.4.22 強制規定最多不能超過 1,048,576 。  www.2cto.com  綜合上面的基本概念:下面的也就不難理解了. 標準輸入 (stdin) :檔案描述符為 0 ,使用 < 或 << ;(你不會非要寫成0<或0<<吧.其實這個也沒錯.不過太累人了)其實可以理解為這個箭頭指向哪裡資料就往哪裡跑.這裡是輸入(stdin).命令就通過<來擷取資料.等於資料是從左邊往命令裡面流. 標準輸出 (stdout):檔案描述符為 1 ,使用 > 或 >> ;(你不會非要寫成1>或1>>吧.其實這個也沒錯.不過太累人了)輸出的時候當如不能用<或者<<,因為命令總是在前面嘛.這裡命令要輸出資料.所以資料的來源是命令,資料就會隨著箭頭指向你給的方向. 標準錯誤輸出(stderr):檔案描述符為 2 ,使用 2> 或 2>>; 再舉例說明: 首先command >file 2>file 的意思是將命令所產生的標準輸出資訊,和錯誤的輸出資訊送到file中.command >file 2>file 這樣的寫法,stdout和stderr都直接送到file中, file會被開啟兩次,這樣stdout和stderr會互相覆蓋,這樣寫相當使用了兩個同時去搶佔file的管道.定向了2次. 那如果使用command >file 2>&1 這條命令就將stdout直接送向file, stderr 繼承了第一次重新導向(FD1)到管道後,再被送往file,此時,file 只被開啟了一次,也只使用了一個管道FD1,它包括了stdout和stderr的內容.還可以這樣理解.想是把file用管道接通了標準輸出.然後把2代表的標準錯誤輸出接到1代表的標準資訊輸出上面.就都通向了file了.  從IO效率上,前一條命令的效率要比後面一條的命令效率要低,所以在編寫shell指令碼的時候,較多的時候我們會用command > file 2>&1 這樣的寫法. 在看看一個執行個體(加深相關的理解,此執行個體引用網上部落格.說是intel的筆試題): 問題:下面程式的輸出是什嗎?(intel筆試2011)  www.2cto.com  1int main(){2  fprintf(stdout,"Hello ");3  fprintf(stderr,"World!");4  return0;5}然後發現輸出是World!Hello而不是:Hello World! 這是為什麼呢?在預設情況下,stdout是行緩衝的,他的輸出會放在一個buffer裡面,只有到換行的時候,才會輸出到螢幕。而stderr是無緩衝的,會直接輸出,舉例來說就是printf(stdout, "xxxx") 和 printf(stdout, "xxxx\n"),前者會憋住,直到遇到新行才會一起輸出。而printf(stderr, "xxxxx"),不管有麼有\n,都輸出. 最後:  www.2cto.com  看看什麼叫/dev/null 1UFO@UFO~:cd /dev2UFO<a href="www.2cto.com" class="referer" target="_blank">@UFO</a> :/dev$ls -l null3crw-rw-rw-  1 root root 1, 3 Feb 14  2012 null看到了吧?是個字元裝置檔案(c).而這個東西呢?你可以叫他"黑洞", Blackhole?NO.不是天文學裡面的黑洞.它非常等價於一個唯寫檔案. 所有寫入它的內容都會永遠丟失. 而嘗試從它那兒讀取內容則什麼也讀不到. 然而, /dev/null 對命令列和指令碼都非常的有用.再來看看在glibc庫的stdio.h標頭檔中: 1#define    stdin    (&__sF[0])2#define    stdout    (&__sF[1])3#define    stderr    (&__sF[2])比如  www.2cto.com  1fprintf(stderr, "UFO\n");//那麼將把"UFO"作為標準錯誤輸出在shell命令中,0,1和2分別對應glibc中的stdin,stdout和stderr,上面我們已經大概瞭解到了: 0 對應stdin     即標準輸入1 對應stdout    即標準輸出2 對應stderr    即標準錯誤輸出所以>/dev/null表示將程式通過printf或者fprintf列印到handle為1的stdout檔案的資訊,送到/dev/null空洞檔案,/dev/null節點對應的kernel實現就是直接返回寫入的位元組數,所以程式認為成功儲存到/dev/null了,但是>/dev/null這個操作不能將fprintf(stderr, "UFO\n")列印到stderr上的字串送到>/dev/null下,所以必須使用2>&1命令,表示shell將送到2 stderr中的資料轉送到1 stdout中,所以這樣stderr中會顯示到terminal上的資訊也將被轉送到/dev/null下了. 又看個執行個體吧: 01luther@gliethttp:~$ cat a.c02#include <stdio.h>03int main(int argc, char *argv[])04{05    fprintf(stdout,"luther stdout\n");06    fprintf(stderr,"luther stderr\n");07    return 0;08}09luther@gliethttp:~$ gcc a.c10luther@gliethttp:~$ ./a.out11luther stdout12  www.2cto.com  luther stderr13luther@gliethttp:~$ ./a.out >/dev/null14luther stderr//可以看到>/dev/null操作並不會將stderr資訊送到/dev/null下寫一個test.sh指令碼 01luther@gliethttp:~$ chmod +x test.sh02luther@gliethttp:~$ cat test.sh03exec ./a.out >/dev/null04luther@gliethttp:~$ ./test.sh05luther stderr       //可以看到shell也不會將stderr資訊送到/dev/null下06luther@gliethttp:~$ cat test.sh07exec ./a.out >/dev/null 2>&108luther@gliethttp:~$ ./test.sh09luther@gliethttp:~$ //什麼也沒有輸出,stderr資訊被2>&1命令成功變為stdout資訊,進而送入了/dev/null淹沒10luther@gliethttp:~$ cat test.sh11exec ./a.out >/dev/null 1>&212./test.sh13luther stdout14luther stderr15luther@gliethttp:~$當然對於tee操作也同樣存在如上問題,如果列印到stderr的log將不能被tee操作捕獲,所以可以將stderr重新導向到stdout來解決這個問題,  www.2cto.com  繼續看看執行個體:01luther@gliethttp:~$ ./a.out02luther stdout03luther stderr04luther@gliethttp:~$ ./a.out|tee luther.txt05luther stdout06luther stderr07luther@gliethttp:~$ cat luther.txt08luther stdout09luther@gliethttp:~$ ./a.out 2>&1|tee luther.txt10luther stderr11luther stdout12luther@gliethttp:~$ cat luther.txt13luther stderr14luther stdout15luther@gliethttp:~$ ./a.out 1>&2|tee luther.txt16luther stderr17luther stdout18luther@gliethttp:~$ cat luther.txt19luther@gliethttp:~$ //啥東西都沒有,因為stdout被定向到stderr,所以所有log資訊都不能被tee捕獲20 對於1>&2因為作為一個命令將被shell解析,所以放在哪裡都可以,1>&2將影響到該組命令中所有的log輸出,比如:21luther@gliethttp:~$ 1>&2 ./a.out|tee luther.txt22  www.2cto.com  luther stderr23luther stdout24luther@gliethttp:~$ ./a.out 1>2 //只輸出stderr25luther stderr26luther@gliethttp:~$ ./a.out 2>1 //只輸出stdout27luther stdout OK.不想寫了.忙的很.上面的文章我只是添油加醋的把網路上很多因為大抄而散落各地的文章亂七八糟的整合了一下.所以我是站在巨人的肩上來了一次大抄.感謝那些巨人吧.其實上面提到的概念估計就這篇整合文章也不能完全解釋的非常徹底.相關的概念更多的還在於實踐和看文檔再加自己的大腦過濾與思考.希望和大家探討!
 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.