掘金標題中不能出現<>符號。。。各位看官不要介意。。。
背景
我們經常能在shell指令碼中發現>/dev/null 2>&1
這樣的語句。以前的我並沒有去深入地理解這段命令的作用,照搬照用,直到上週我將這段命令不小心寫成了2>&1 >/dev/null
,出了一點小問題之後,我才開始去了解這段命令背後的“玄機”。
shell重定向介紹
就像我們平時寫的程式一樣,一段程式會處理外部的輸入,然後將運算結果輸出到指定的位置。在互動式的程式中,輸入來自使用者的鍵盤和滑鼠,結果輸出到使用者的螢幕,甚至播放裝置中。而對於某些後臺執行的程式,輸入可能來自於外部的一些檔案,運算的結果通常又寫到其他的檔案中。而且程式在執行的過程中,會有一些關鍵性的資訊,比如異常堆疊,外部介面呼叫情況等,這些都會統統寫到日誌檔案裡。
shell指令碼也一樣,但是我們一般在使用shell命令的時候,更多地還是通過鍵盤輸入,然後在螢幕上檢視命令的執行結果。如果某些情況下,我們需要將shell命令的執行結果儲存到檔案中,那麼我們就需要使用輸入輸出的重定向功能。
檔案描述符
當執行shell命令時,會預設開啟3個檔案,每個檔案有對應的檔案描述符來方便我們使用:
型別 | 檔案描述符 | 預設情況 | 對應檔案控制程式碼位置 |
---|---|---|---|
標準輸入(standard input) | 0 | 從鍵盤獲得輸入 | /proc/slef/fd/0 |
標準輸出(standard output) | 1 | 輸出到螢幕(即控制檯) | /proc/slef/fd/1 |
錯誤輸出(error output) | 2 | 輸出到螢幕(即控制檯) | /proc/slef/fd/2 |
所以我們平時在執行shell命令中,都預設是從鍵盤獲得輸入,並且將結果輸出到控制檯上。但是我們可以通過更改檔案描述符預設的指向,從而實現輸入輸出的重定向。比如我們將1指向檔案,那麼標準的輸出就會輸出到檔案中。
輸出重定向
輸出重定向的使用方式很簡單,基本的一些命令如下:
命令 | 介紹 |
---|---|
command >filename | 把標準輸出重定向到新檔案中 |
command 1>filename | 同上 |
command >>filename | 把標準輸出追加到檔案中 |
command 1>>filename | 同上 |
command 2>filename | 把標準錯誤重定向到新檔案中 |
command 2>>filename | 把標準錯誤追加到新檔案中 |
我們使用>
或者>>
對輸出進行重定向。符號的左邊表示檔案描述符,如果沒有的話表示1,也就是標準輸出,符號的右邊可以是一個檔案,也可以是一個輸出裝置。當使用>
時,會判斷右邊的檔案存不存在,如果存在的話就先刪除,然後建立一個新的檔案,不存在的話則直接建立。但是當使用>>
進行追加時,則不會刪除原來已經存在的檔案。
為了更好地理解輸出重定向,感受重定向的“魅力”,我們看一下以下的例子:我們建立一個測試目錄,目錄下面僅有一個a.txt檔案。
# tree
.
└── a.txt
0 directories, 1 file
# ls a.txt b.txt
ls: 無法訪問b.txt: 沒有那個檔案或目錄
a.txt複製程式碼
在我們執行ls a.txt b.txt
之後,一共有兩種輸出,其中ls: 無法訪問b.txt: 沒有那個檔案或目錄
是錯誤輸出,a.txt
是標準輸出。
# ls a.txt b.txt 1>out
ls: 無法訪問b.txt: 沒有那個檔案或目錄
# cat out
a.txt
# ls a.txt b.txt >>out
ls: 無法訪問b.txt: 沒有那個檔案或目錄
# cat out
a.txt
a.txt複製程式碼
在上述命令中,我們將原來的標準輸出重定向到了out檔案中,所以控制檯只剩下了錯誤提示。並且當執行了追加操作時,out檔案的內容非但沒有被清空,反而又多了一條a.txt
。
同理,我們也可以將錯誤輸出重定向到檔案中:
# ls a.txt b.txt 2>err
a.txt
# cat err
ls: 無法訪問b.txt: 沒有那個檔案或目錄
# ls a.txt b.txt >out 2>err
# cat out
a.txt
# cat err
ls: 無法訪問b.txt: 沒有那個檔案或目錄複製程式碼
看到這裡,朋友們可能會發現>out 2>err
和我們在一開頭提到的>/dev/null 2>&1
已經很像了,別急,這待會再說。
輸入重定向
在理解了輸出重定向之後,理解輸入重定向就會容易得多。對輸入重定向的基本命令如下:
命令 | 介紹 |
---|---|
command <filename | 以filename檔案作為標準輸入 |
command 0<filename | 同上 |
command <<delimiter | 從標準輸入中讀入,直到遇到delimiter分隔符 |
我們使用<
對輸入做重定向,如果符號左邊沒有寫值,那麼預設就是0。
我們這次以cat命令為例,如果cat後面沒有跟檔名的話,那它的作用就是將標準輸入(比如鍵盤)回顯到標準輸出(比如螢幕)上:
# cat
123
123
test
test複製程式碼
我們可以將利用輸入重定向,將我們在鍵盤上敲入的字元寫入到檔案中。我們需要使用ctrl+c來結束輸入:
# cat >out
123
test
^C
# cat out
123
test複製程式碼
好了,此時我們覺得自己在鍵盤上敲比較累,還是直接讓cat讀取一個檔案吧。那麼我們需要利用輸入重定向:
# cat input
aaa
111
# cat >out < input
# cat out
aaa
111複製程式碼
神奇的事情發生了,out檔案裡面的內容被替換成了input檔案裡的內容。那麼<<
又是什麼作用呢?我們再看:
# cat >out << end
> 123
> test
> end
# cat out
123
test複製程式碼
我們看到,當我們輸入完cat >out <<end
,然後敲下回車之後,命令並沒有結束,此時cat命令像一開始一樣,等待你給它輸入資料。然後當我們敲入end
之後,cat命令就結束了。end
之前輸入的字元都已經被寫入到了out檔案中。這就是輸入分割符的作用。
高階用法
重定向繫結
好了,在有了以上知識的基礎上,我們再來看開頭提到的>/dev/null 2>&1
。這條命令其實分為兩命令,一個是>/dev/null
,另一個是2>&1
。
1. >/dev/null
這條命令的作用是將標準輸出1重定向到/dev/null中。/dev/null代表linux的空裝置檔案,所有往這個檔案裡面寫入的內容都會丟失,俗稱“黑洞”。那麼執行了>/dev/null
之後,標準輸出就會不再存在,沒有任何地方能夠找到輸出的內容。
2. 2>&1
這條命令用到了重定向繫結,採用&可以將兩個輸出繫結在一起。這條命令的作用是錯誤輸出將和標準輸出同用一個檔案描述符,說人話就是錯誤輸出將會和標準輸出輸出到同一個地方。
linux在執行shell命令之前,就會確定好所有的輸入輸出位置,並且從左到右依次執行重定向的命令,所以>/dev/null 2>&1
的作用就是讓標準輸出重定向到/dev/null中(丟棄標準輸出),然後錯誤輸出由於重用了標準輸出的描述符,所以錯誤輸出也被定向到了/dev/null中,錯誤輸出同樣也被丟棄了。執行了這條命令之後,該條shell命令將不會輸出任何資訊到控制檯,也不會有任何資訊輸出到檔案中。
>/dev/null 2>&1 VS 2>&1 >/dev/null
再回到文章的開頭,我說我弄反了>/dev/null
和2>&1
拼裝的順序,導致出了一點小問題。乍眼看這兩條命令貌似是等同的,但其實大為不同。剛才提到了,linux在執行shell命令之前,就會確定好所有的輸入輸出位置,並且從左到右依次執行重定向的命令。那麼我們同樣從左到右地來分析2>&1 >/dev/null
:
- 2>&1,將錯誤輸出繫結到標準輸出上。由於此時的標準輸出是預設值,也就是輸出到螢幕,所以錯誤輸出會輸出到螢幕。
- >/dev/null,將標準輸出1重定向到/dev/null中。
我們用一個表格來更好地說明這兩條命令的區別:
命令 | 標準輸出 | 錯誤輸出 |
---|---|---|
>/dev/null 2>&1 | 丟棄 | 丟棄 |
2>&1 >/dev/null | 丟棄 | 螢幕 |
>/dev/null 2>&1 VS >/dev/null 2>/dev/null
那麼可能會有些同學會疑問,為什麼要用重定向繫結,而不是像>/dev/null 2>/dev/null
這樣子重複一遍呢。
為了回答這個問題,我們回到剛才介紹輸出重定向的場景。我們嘗試將標準輸出和錯誤輸出都定向到out檔案中:
# ls a.txt b.txt >out 2>out
# cat out
a.txt
�法訪問b.txt: 沒有那個檔案或目錄複製程式碼
WTF?竟然出現了亂碼,這是為啥呢?這是因為採用這種寫法,標準輸出和錯誤輸出會搶佔往out檔案的管道,所以可能會導致輸出內容的時候出現缺失、覆蓋等情況。現在是出現了亂碼,有時候也有可能出現只有error資訊或者只有正常資訊的情況。不管怎麼說,採用這種寫法,最後的情況是無法預估的。
而且,由於out檔案被開啟了兩次,兩個檔案描述符會搶佔性的往檔案中輸出內容,所以整體IO效率不如>/dev/null 2>&1
來得高。
nohup結合
我們經常使用nohup command &
命令形式來啟動一些後臺程式,比如一些java服務:
# nohup java -jar xxxx.jar &複製程式碼
為了不讓一些執行資訊輸出到前臺(控制檯),我們還會加上剛才提到的>/dev/null 2>&1
命令來丟棄所有的輸出:
# nohup java -jar xxxx.jar >/dev/null 2>&1 &複製程式碼
總結
本文主要介紹了linux重定向的原理以及一些基本命令,並且詳細地分析了>/dev/null 2>&1
這個命令以及一些注意點。
總而言之,在工作中用到最多的就是nohup command >/dev/null 2>&1 &
命令,希望大家能夠好好掌握。
參考資料
小問題
接著本文的場景,下面命令,錯誤輸出會輸出到什麼地方呢?同學們可以在評論區留言回答哦~
# ls a.txt b.txt 2>&1 >/dev/null 2>&1複製程式碼