背景
我們經(jīng)常能在shell腳本中發(fā)現(xiàn)>/dev/null 2>&1這樣的語句。以前的我并沒有去深入地理解這段命令的作用,照搬照用隐孽,直到上周我將這段命令不小心寫成了2>&1 >/dev/null绊诲,出了一點小問題之后抗俄,我才開始去了解這段命令背后的“玄機(jī)”跟压。
shell重定向介紹
就像我們平時寫的程序一樣,一段程序會處理外部的輸入钾虐,然后將運算結(jié)果輸出到指定的位置。在交互式的程序中,輸入來自用戶的鍵盤和鼠標(biāo)瘦锹,結(jié)果輸出到用戶的屏幕听绳,甚至播放設(shè)備中。而對于某些后臺運行的程序,輸入可能來自于外部的一些文件,運算的結(jié)果通常又寫到其他的文件中。而且程序在運行的過程中,會有一些關(guān)鍵性的信息,比如異常堆棧,外部接口調(diào)用情況等米绕,這些都會統(tǒng)統(tǒng)寫到日志文件里。
shell腳本也一樣,但是我們一般在使用shell命令的時候贵白,更多地還是通過鍵盤輸入,然后在屏幕上查看命令的執(zhí)行結(jié)果崩泡。如果某些情況下角撞,我們需要將shell命令的執(zhí)行結(jié)果存儲到文件中谒所,那么我們就需要使用輸入輸出的重定向功能。
文件描述符
當(dāng)執(zhí)行shell命令時梆造,會默認(rèn)打開3個文件镇辉,每個文件有對應(yīng)的文件描述符來方便我們使用:
類型 文件描述符 默認(rèn)情況 對應(yīng)文件句柄位置
標(biāo)準(zhǔn)輸入(standard input) 0 從鍵盤獲得輸入 /proc/slef/fd/0
標(biāo)準(zhǔn)輸出(standard output) 1 輸出到屏幕(即控制臺) /proc/slef/fd/1
錯誤輸出(error output) 2 輸出到屏幕(即控制臺) /proc/slef/fd/2
所以我們平時在執(zhí)行shell命令中屡穗,都默認(rèn)是從鍵盤獲得輸入,并且將結(jié)果輸出到控制臺上屹逛。但是我們可以通過更改文件描述符默認(rèn)的指向础废,從而實現(xiàn)輸入輸出的重定向汛骂。比如我們將1指向文件,那么標(biāo)準(zhǔ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)錯誤重定向到新文件中
command 2>>filename 把標(biāo)準(zhǔn)錯誤追加到新文件中
我們使用>或者>>對輸出進(jìn)行重定向。符號的左邊表示文件描述符蒿讥,如果沒有的話表示1蝶念,也就是標(biāo)準(zhǔn)輸出,符號的右邊可以是一個文件芋绸,也可以是一個輸出設(shè)備媒殉。當(dāng)使用>時,會判斷右邊的文件存不存在侥钳,如果存在的話就先刪除适袜,然后創(chuàng)建一個新的文件,不存在的話則直接創(chuàng)建舷夺。但是當(dāng)使用>>進(jìn)行追加時苦酱,則不會刪除原來已經(jīng)存在的文件。
為了更好地理解輸出重定向给猾,感受重定向的“魅力”疫萤,我們看一下以下的例子:我們創(chuàng)建一個測試目錄,目錄下面僅有一個a.txt文件敢伸。
# tree
.
└── a.txt
0 directories, 1 file
# ls a.txt b.txt
ls: 無法訪問b.txt: 沒有那個文件或目錄
a.txt
在我們執(zhí)行l(wèi)s a.txt b.txt之后扯饶,一共有兩種輸出,其中l(wèi)s: 無法訪問b.txt: 沒有那個文件或目錄是錯誤輸出池颈,a.txt是標(biāo)準(zhǔn)輸出尾序。
# 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
在上述命令中,我們將原來的標(biāo)準(zhǔn)輸出重定向到了out文件中躯砰,所以控制臺只剩下了錯誤提示每币。并且當(dāng)執(zhí)行了追加操作時,out文件的內(nèi)容非但沒有被清空琢歇,反而又多了一條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: 沒有那個文件或目錄
看到這里李茫,朋友們可能會發(fā)現(xiàn)>out 2>err和我們在一開頭提到的>/dev/null 2>&1已經(jīng)很像了揭保,別急,這待會再說魄宏。
輸入重定向
在理解了輸出重定向之后秸侣,理解輸入重定向就會容易得多。對輸入重定向的基本命令如下:
命令 介紹
command <filename 以filename文件作為標(biāo)準(zhǔn)輸入
command 0<filename 同上
command <<delimiter 從標(biāo)準(zhǔn)輸入中讀入,直到遇到delimiter分隔符
我們使用<對輸入做重定向味榛,如果符號左邊沒有寫值方篮,那么默認(rèn)就是0。
我們這次以cat命令為例励负,如果cat后面沒有跟文件名的話,那它的作用就是將標(biāo)準(zhǔn)輸入(比如鍵盤)回顯到標(biāo)準(zhǔn)輸出(比如屏幕)上:
# cat
123
123
test
test
我們可以將利用輸入重定向匕得,將我們在鍵盤上敲入的字符寫入到文件中继榆。我們需要使用ctrl+c來結(jié)束輸入:
# cat >out
123
test
^C
# cat out
123
test
好了,此時我們覺得自己在鍵盤上敲比較累汁掠,還是直接讓cat讀取一個文件吧略吨。那么我們需要利用輸入重定
# cat input
aaa
111
# cat >out <input
# cat out
aaa
111
神奇的事情發(fā)生了,out文件里面的內(nèi)容被替換成了input文件里的內(nèi)容考阱。那么<<又是什么作用呢翠忠?我們再看:
# cat >out <<end
> 123
> test
> end
# cat out
123
test
我們看到,當(dāng)我們輸入完cat >out <<end乞榨,然后敲下回車之后秽之,命令并沒有結(jié)束,此時cat命令像一開始一樣吃既,等待你給它輸入數(shù)據(jù)考榨。然后當(dāng)我們敲入end之后,cat命令就結(jié)束了鹦倚。end之前輸入的字符都已經(jīng)被寫入到了out文件中河质。這就是輸入分割符的作用。
高級用法
重定向綁定
好了震叙,在有了以上知識的基礎(chǔ)上掀鹅,我們再來看開頭提到的>/dev/null 2>&1。這條命令其實分為兩命令媒楼,一個是>/dev/null乐尊,另一個是2>&1。
1. >/dev/null
這條命令的作用是將標(biāo)準(zhǔn)輸出1重定向到/dev/null中匣砖。/dev/null代表linux的空設(shè)備文件科吭,所有往這個文件里面寫入的內(nèi)容都會丟失,俗稱“黑洞”猴鲫。那么執(zhí)行了>/dev/null之后对人,標(biāo)準(zhǔn)輸出就會不再存在,沒有任何地方能夠找到輸出的內(nèi)容拂共。
2. 2>&1
這條命令用到了重定向綁定牺弄,采用&可以將兩個輸出綁定在一起。這條命令的作用是錯誤輸出將和標(biāo)準(zhǔn)輸出同用一個文件描述符宜狐,說人話就是錯誤輸出將會和標(biāo)準(zhǔn)輸出輸出到同一個地方势告。
linux在執(zhí)行shell命令之前蛇捌,就會確定好所有的輸入輸出位置,并且從左到右依次執(zhí)行重定向的命令咱台,所以>/dev/null 2>&1的作用就是讓標(biāo)準(zhǔn)輸出重定向到/dev/null中(丟棄標(biāo)準(zhǔn)輸出)络拌,然后錯誤輸出由于重用了標(biāo)準(zhǔn)輸出的描述符,所以錯誤輸出也被定向到了/dev/null中回溺,錯誤輸出同樣也被丟棄了春贸。執(zhí)行了這條命令之后,該條shell命令將不會輸出任何信息到控制臺遗遵,也不會有任何信息輸出到文件中萍恕。
>/dev/null 2>&1 VS 2>&1 >/dev/null
再回到文章的開頭,我說我弄反了>/dev/null和2>&1拼裝的順序车要,導(dǎo)致出了一點小問題允粤。乍眼看這兩條命令貌似是等同的,但其實大為不同翼岁。剛才提到了类垫,linux在執(zhí)行shell命令之前,就會確定好所有的輸入輸出位置琅坡,并且從左到右依次執(zhí)行重定向的命令阔挠。那么我們同樣從左到右地來分析2>&1 >/dev/null:
2>&1,將錯誤輸出綁定到標(biāo)準(zhǔn)輸出上脑蠕。由于此時的標(biāo)準(zhǔn)輸出是默認(rèn)值购撼,也就是輸出到屏幕,所以錯誤輸出會輸出到屏幕谴仙。
>/dev/null迂求,將標(biāo)準(zhǔn)輸出1重定向到/dev/null中。
我們用一個表格來更好地說明這兩條命令的區(qū)別:
命令 標(biāo)準(zhǔn)輸出 錯誤輸出
>/dev/null 2>&1 丟棄 丟棄
2>&1 >/dev/null 丟棄 屏幕
>/dev/null 2>&1 VS >/dev/null 2>/dev/null
那么可能會有些同學(xué)會疑問晃跺,為什么要用重定向綁定揩局,而不是像>/dev/null 2>/dev/null這樣子重復(fù)一遍呢。
為了回答這個問題掀虎,我們回到剛才介紹輸出重定向的場景凌盯。我們嘗試將標(biāo)準(zhǔn)輸出和錯誤輸出都定向到out文件中:
1
2
3
4
# ls a.txt b.txt >out 2>out
# cat out
a.txt
?法訪問b.txt: 沒有那個文件或目錄
WTF?竟然出現(xiàn)了亂碼烹玉,這是為啥呢驰怎?這是因為采用這種寫法,標(biāo)準(zhǔn)輸出和錯誤輸出會搶占往out文件的管道二打,所以可能會導(dǎo)致輸出內(nèi)容的時候出現(xiàn)缺失县忌、覆蓋等情況。現(xiàn)在是出現(xiàn)了亂碼,有時候也有可能出現(xiàn)只有error信息或者只有正常信息的情況症杏。不管怎么說装获,采用這種寫法,最后的情況是無法預(yù)估的厉颤。
而且穴豫,由于out文件被打開了兩次,兩個文件描述符會搶占性的往文件中輸出內(nèi)容逼友,所以整體IO效率不如>/dev/null 2>&1來得高绩郎。
nohup結(jié)合
我們經(jīng)常使用nohup command &命令形式來啟動一些后臺程序,比如一些java服務(wù):
1
# nohup java -jar xxxx.jar &
為了不讓一些執(zhí)行信息輸出到前臺(控制臺)翁逞,我們還會加上剛才提到的>/dev/null 2>&1命令來丟棄所有的輸出:
1
# nohup java -jar xxxx.jar >/dev/null 2>&1 &
總結(jié)
本文主要介紹了linux重定向的原理以及一些基本命令,并且詳細(xì)地分析了>/dev/null 2>&1這個命令以及一些注意點溉仑。
總而言之挖函,在工作中用到最多的就是nohup command >/dev/null 2>&1 &命令,希望大家能夠好好掌握浊竟。