背景
我們經(jīng)常能在shell腳本中發(fā)現(xiàn)>/dev/null 2>&1
這樣的語(yǔ)句。以前的我并沒(méi)有去深入地理解這段命令的作用合陵,照搬照用枢赔,直到上周我將這段命令不小心寫(xiě)成了2>&1 >/dev/null
,出了一點(diǎn)小問(wèn)題之后拥知,我才開(kāi)始去了解這段命令背后的“玄機(jī)”踏拜。
shell重定向介紹
就像我們平時(shí)寫(xiě)的程序一樣,一段程序會(huì)處理外部的輸入低剔,然后將運(yùn)算結(jié)果輸出到指定的位置速梗。在交互式的程序中,輸入來(lái)自用戶的鍵盤(pán)和鼠標(biāo)襟齿,結(jié)果輸出到用戶的屏幕姻锁,甚至播放設(shè)備中。而對(duì)于某些后臺(tái)運(yùn)行的程序猜欺,輸入可能來(lái)自于外部的一些文件位隶,運(yùn)算的結(jié)果通常又寫(xiě)到其他的文件中。而且程序在運(yùn)行的過(guò)程中开皿,會(huì)有一些關(guān)鍵性的信息涧黄,比如異常堆棧篮昧,外部接口調(diào)用情況等,這些都會(huì)統(tǒng)統(tǒng)寫(xiě)到日志文件里笋妥。
shell腳本也一樣懊昨,但是我們一般在使用shell命令的時(shí)候,更多地還是通過(guò)鍵盤(pán)輸入春宣,然后在屏幕上查看命令的執(zhí)行結(jié)果酵颁。如果某些情況下,我們需要將shell命令的執(zhí)行結(jié)果存儲(chǔ)到文件中月帝,那么我們就需要使用輸入輸出的重定向功能躏惋。
文件描述符
當(dāng)執(zhí)行shell命令時(shí),會(huì)默認(rèn)打開(kāi)3個(gè)文件嫁赏,每個(gè)文件有對(duì)應(yīng)的文件描述符來(lái)方便我們使用:
類型 | 文件描述符 | 默認(rèn)情況 | 對(duì)應(yīng)文件句柄位置 |
---|---|---|---|
標(biāo)準(zhǔn)輸入(standard input) | 0 | 從鍵盤(pán)獲得輸入 | /proc/slef/fd/0 |
標(biāo)準(zhǔn)輸出(standard output) | 1 | 輸出到屏幕(即控制臺(tái)) | /proc/slef/fd/1 |
錯(cuò)誤輸出(error output) | 2 | 輸出到屏幕(即控制臺(tái)) | /proc/slef/fd/2 |
所以我們平時(shí)在執(zhí)行shell命令中其掂,都默認(rèn)是從鍵盤(pán)獲得輸入油挥,并且將結(jié)果輸出到控制臺(tái)上潦蝇。但是我們可以通過(guò)更改文件描述符默認(rèn)的指向,從而實(shí)現(xiàn)輸入輸出的重定向深寥。比如我們將1指向文件攘乒,那么標(biāo)準(zhǔn)的輸出就會(huì)輸出到文件中。
輸出重定向
輸出重定向的使用方式很簡(jiǎn)單惋鹅,基本的一些命令如下:
命令 | 介紹 |
---|---|
command >filename | 把標(biāo)準(zhǔn)輸出重定向到新文件中 |
command 1>filename | 同上 |
command >>filename | 把標(biāo)準(zhǔn)輸出追加到文件中 |
command 1>>filename | 同上 |
command 2>filename | 把標(biāo)準(zhǔn)錯(cuò)誤重定向到新文件中 |
command 2>>filename | 把標(biāo)準(zhǔn)錯(cuò)誤追加到新文件中 |
我們使用>
或者>>
對(duì)輸出進(jìn)行重定向则酝。符號(hào)的左邊表示文件描述符,如果沒(méi)有的話表示1闰集,也就是標(biāo)準(zhǔn)輸出沽讹,符號(hào)的右邊可以是一個(gè)文件,也可以是一個(gè)輸出設(shè)備武鲁。當(dāng)使用>
時(shí)爽雄,會(huì)判斷右邊的文件存不存在,如果存在的話就先刪除沐鼠,然后創(chuàng)建一個(gè)新的文件挚瘟,不存在的話則直接創(chuàng)建。但是當(dāng)使用>>
進(jìn)行追加時(shí)饲梭,則不會(huì)刪除原來(lái)已經(jīng)存在的文件乘盖。
為了更好地理解輸出重定向,感受重定向的“魅力”憔涉,我們看一下以下的例子:我們創(chuàng)建一個(gè)測(cè)試目錄订框,目錄下面僅有一個(gè)a.txt文件。
# tree
.
└── a.txt
0 directories, 1 file
# ls a.txt b.txt
ls: 無(wú)法訪問(wèn)b.txt: 沒(méi)有那個(gè)文件或目錄
a.txt
在我們執(zhí)行ls a.txt b.txt
之后兜叨,一共有兩種輸出穿扳,其中ls: 無(wú)法訪問(wèn)b.txt: 沒(méi)有那個(gè)文件或目錄
是錯(cuò)誤輸出藤违,a.txt
是標(biāo)準(zhǔn)輸出。
# ls a.txt b.txt 1>out
ls: 無(wú)法訪問(wèn)b.txt: 沒(méi)有那個(gè)文件或目錄
# cat out
a.txt
# ls a.txt b.txt >>out
ls: 無(wú)法訪問(wèn)b.txt: 沒(méi)有那個(gè)文件或目錄
# cat out
a.txt
a.txt
在上述命令中纵揍,我們將原來(lái)的標(biāo)準(zhǔn)輸出重定向到了out文件中顿乒,所以控制臺(tái)只剩下了錯(cuò)誤提示。并且當(dāng)執(zhí)行了追加操作時(shí)泽谨,out文件的內(nèi)容非但沒(méi)有被清空璧榄,反而又多了一條a.txt
。
同理吧雹,我們也可以將錯(cuò)誤輸出重定向到文件中:
# ls a.txt b.txt 2>err
a.txt
# cat err
ls: 無(wú)法訪問(wèn)b.txt: 沒(méi)有那個(gè)文件或目錄
# ls a.txt b.txt >out 2>err
# cat out
a.txt
# cat err
ls: 無(wú)法訪問(wèn)b.txt: 沒(méi)有那個(gè)文件或目錄
看到這里骨杂,朋友們可能會(huì)發(fā)現(xiàn)>out 2>err
和我們?cè)谝婚_(kāi)頭提到的>/dev/null 2>&1
已經(jīng)很像了,別急雄卷,這待會(huì)再說(shuō)搓蚪。
輸入重定向
在理解了輸出重定向之后,理解輸入重定向就會(huì)容易得多丁鹉。對(duì)輸入重定向的基本命令如下:
命令 | 介紹 |
---|---|
command <filename | 以filename文件作為標(biāo)準(zhǔn)輸入 |
command 0<filename | 同上 |
command <<delimiter | 從標(biāo)準(zhǔn)輸入中讀入妒潭,直到遇到delimiter分隔符 |
我們使用<
對(duì)輸入做重定向,如果符號(hào)左邊沒(méi)有寫(xiě)值揣钦,那么默認(rèn)就是0雳灾。
我們這次以cat命令為例,如果cat后面沒(méi)有跟文件名的話冯凹,那它的作用就是將標(biāo)準(zhǔn)輸入(比如鍵盤(pán))回顯到標(biāo)準(zhǔn)輸出(比如屏幕)上:
# cat
123
123
test
test
我們可以將利用輸入重定向谎亩,將我們?cè)阪I盤(pán)上敲入的字符寫(xiě)入到文件中。我們需要使用ctrl+c來(lái)結(jié)束輸入:
# cat >out
123
test
^C
# cat out
123
test
好了宇姚,此時(shí)我們覺(jué)得自己在鍵盤(pán)上敲比較累匈庭,還是直接讓cat讀取一個(gè)文件吧。那么我們需要利用輸入重定向:
# cat input
aaa
111
# cat >out <input
# cat out
aaa
111
神奇的事情發(fā)生了浑劳,out文件里面的內(nèi)容被替換成了input文件里的內(nèi)容阱持。那么<<
又是什么作用呢?我們?cè)倏矗?/p>
# cat >out <<end
> 123
> test
> end
# cat out
123
test
我們看到呀洲,當(dāng)我們輸入完cat >out <<end
紊选,然后敲下回車之后,命令并沒(méi)有結(jié)束道逗,此時(shí)cat命令像一開(kāi)始一樣兵罢,等待你給它輸入數(shù)據(jù)。然后當(dāng)我們敲入end
之后滓窍,cat命令就結(jié)束了卖词。end
之前輸入的字符都已經(jīng)被寫(xiě)入到了out文件中。這就是輸入分割符的作用。
高級(jí)用法
重定向綁定
好了此蜈,在有了以上知識(shí)的基礎(chǔ)上即横,我們?cè)賮?lái)看開(kāi)頭提到的>/dev/null 2>&1
。這條命令其實(shí)分為兩命令裆赵,一個(gè)是>/dev/null
东囚,另一個(gè)是2>&1
。
1. >/dev/null
這條命令的作用是將標(biāo)準(zhǔn)輸出1重定向到/dev/null中战授。/dev/null代表linux的空設(shè)備文件页藻,所有往這個(gè)文件里面寫(xiě)入的內(nèi)容都會(huì)丟失,俗稱“黑洞”植兰。那么執(zhí)行了>/dev/null
之后份帐,標(biāo)準(zhǔn)輸出就會(huì)不再存在,沒(méi)有任何地方能夠找到輸出的內(nèi)容楣导。
2. 2>&1
這條命令用到了重定向綁定废境,采用&可以將兩個(gè)輸出綁定在一起。這條命令的作用是錯(cuò)誤輸出將和標(biāo)準(zhǔn)輸出同用一個(gè)文件描述符筒繁,說(shuō)人話就是錯(cuò)誤輸出將會(huì)和標(biāo)準(zhǔn)輸出輸出到同一個(gè)地方噩凹。
linux在執(zhí)行shell命令之前,就會(huì)確定好所有的輸入輸出位置膝晾,并且從左到右依次執(zhí)行重定向的命令栓始,所以>/dev/null 2>&1
的作用就是讓標(biāo)準(zhǔn)輸出重定向到/dev/null中(丟棄標(biāo)準(zhǔn)輸出),然后錯(cuò)誤輸出由于重用了標(biāo)準(zhǔn)輸出的描述符血当,所以錯(cuò)誤輸出也被定向到了/dev/null中,錯(cuò)誤輸出同樣也被丟棄了禀忆。執(zhí)行了這條命令之后臊旭,該條shell命令將不會(huì)輸出任何信息到控制臺(tái),也不會(huì)有任何信息輸出到文件中箩退。
>/dev/null 2>&1 VS 2>&1 >/dev/null
再回到文章的開(kāi)頭离熏,我說(shuō)我弄反了>/dev/null
和2>&1
拼裝的順序,導(dǎo)致出了一點(diǎn)小問(wèn)題戴涝。乍眼看這兩條命令貌似是等同的滋戳,但其實(shí)大為不同。剛才提到了啥刻,linux在執(zhí)行shell命令之前奸鸯,就會(huì)確定好所有的輸入輸出位置,并且從左到右依次執(zhí)行重定向的命令可帽。那么我們同樣從左到右地來(lái)分析2>&1 >/dev/null
:
- 2>&1娄涩,將錯(cuò)誤輸出綁定到標(biāo)準(zhǔn)輸出上。由于此時(shí)的標(biāo)準(zhǔn)輸出是默認(rèn)值映跟,也就是輸出到屏幕蓄拣,所以錯(cuò)誤輸出會(huì)輸出到屏幕扬虚。
- >/dev/null,將標(biāo)準(zhǔn)輸出1重定向到/dev/null中球恤。
我們用一個(gè)表格來(lái)更好地說(shuō)明這兩條命令的區(qū)別:
命令 | 標(biāo)準(zhǔn)輸出 | 錯(cuò)誤輸出 |
---|---|---|
>/dev/null 2>&1 | 丟棄 | 丟棄 |
2>&1 >/dev/null | 丟棄 | 屏幕 |
>/dev/null 2>&1 VS >/dev/null 2>/dev/null
那么可能會(huì)有些同學(xué)會(huì)疑問(wèn)辜昵,為什么要用重定向綁定,而不是像>/dev/null 2>/dev/null
這樣子重復(fù)一遍呢咽斧。
為了回答這個(gè)問(wèn)題路鹰,我們回到剛才介紹輸出重定向的場(chǎng)景。我們嘗試將標(biāo)準(zhǔn)輸出和錯(cuò)誤輸出都定向到out文件中:
# ls a.txt b.txt >out 2>out
# cat out
a.txt
?法訪問(wèn)b.txt: 沒(méi)有那個(gè)文件或目錄
WTF收厨?竟然出現(xiàn)了亂碼晋柱,這是為啥呢?這是因?yàn)椴捎眠@種寫(xiě)法诵叁,標(biāo)準(zhǔn)輸出和錯(cuò)誤輸出會(huì)搶占往out文件的管道雁竞,所以可能會(huì)導(dǎo)致輸出內(nèi)容的時(shí)候出現(xiàn)缺失、覆蓋等情況∨《睿現(xiàn)在是出現(xiàn)了亂碼碑诉,有時(shí)候也有可能出現(xiàn)只有error信息或者只有正常信息的情況。不管怎么說(shuō)侥锦,采用這種寫(xiě)法进栽,最后的情況是無(wú)法預(yù)估的。
而且恭垦,由于out文件被打開(kāi)了兩次快毛,兩個(gè)文件描述符會(huì)搶占性的往文件中輸出內(nèi)容,所以整體IO效率不如>/dev/null 2>&1
來(lái)得高番挺。
nohup結(jié)合
我們經(jīng)常使用nohup command &
命令形式來(lái)啟動(dòng)一些后臺(tái)程序唠帝,比如一些java服務(wù):
# nohup java -jar xxxx.jar &
為了不讓一些執(zhí)行信息輸出到前臺(tái)(控制臺(tái)),我們還會(huì)加上剛才提到的>/dev/null 2>&1
命令來(lái)丟棄所有的輸出:
# nohup java -jar xxxx.jar >/dev/null 2>&1 &
總結(jié)
本文主要介紹了linux重定向的原理以及一些基本命令玄柏,并且詳細(xì)地分析了>/dev/null 2>&1
這個(gè)命令以及一些注意點(diǎn)襟衰。
總而言之,在工作中用到最多的就是nohup command >/dev/null 2>&1 &
命令粪摘,希望大家能夠好好掌握瀑晒。
參考資料
小問(wèn)題
接著本文的場(chǎng)景,下面命令徘意,錯(cuò)誤輸出會(huì)輸出到什么地方呢苔悦?同學(xué)們可以在評(píng)論區(qū)留言回答哦~
# ls a.txt b.txt 2>&1 >/dev/null 2>&1