資料流重導向就是將某個指令執行後應該要出現在螢幕上的資料, 給他傳輸到其它的地方,例如檔案或者是裝置 (例如印表機之類的!)!這玩意兒在 Linux 的文字模式底下可重要的! 尤其是如果我們想要將某些資料儲存下來時,就更有用了!
一般來說,如果你要執行一個指令,通常他會是這樣的:
我們執行一個指令的時候,這個指令可能會由檔案讀入資料,經過處理之後,再將資料輸出到螢幕上。 在圖中, standard output 與 standard error 分別代表標準輸出與標準錯誤輸出, 這兩個玩意兒預設都是輸出到螢幕上面來的啊!舉個簡單例子來說, 我們下達『 cat /etc/crontab /etc/vbirdsay 』這個指令時,cat 會由 /etc/crontab 與 /etc/vbirdsay 讀入資料, 然後再將資料輸出到螢幕上,不過,因為系統本來就不存在 /etc/vbirdsay 這個檔案, 所以就會顯示錯誤訊息,這個錯誤訊息也會輸出到螢幕上來喔! 在這樣的過程當中,我們可以將 standard error (簡稱 stderr) 與 standard output (簡稱 stdout) 給他傳送到其它不同的地方,而不是螢幕上頭!傳送的目標處,通常是檔案或者是裝置! 而傳送的指令則是如下所示:
1. 標準輸入(stdin) :代碼為 0 ,使用 < 或 << ;
2. 標準輸出(stdout):代碼為 1 ,使用 > 或 >> ;
3. 標準錯誤輸出(stderr):代碼為 2 ,使用 2> 或 2>> ;
舉例來說,如果我想要將我目前根目錄下所有的目錄都記錄下來的話,也就是說,將 ls -l / 這個指令的輸出結果儲存下來,就可以:
[root@linux ~]# ls -l / > ~/rootfile
# 本來 ls -l / 會將根目錄的資料列出到螢幕上;
# 現在我使用了 > ~/rootfile 後,則本來應該在螢幕上出現的資料
# 就會被『重新導向』到 ~/rootfile 檔案內了!就可以將該資料儲存!
此時,原本應該在螢幕上面出現的資料通通不見去~因為那些資料都被寫入到 ~/rootfile 去了! 當然,那個檔案的檔名隨便你取啦~如果你下達:『 cat ~/rootfile 』就可以看到原本應該在螢幕上面的資料囉。 那麼如果我再次下達:『 ls -l /home > ~/rootfile 』後,那麼那個 ~/rootfile 檔案的內容變成什嗎? 呵呵!變成『僅有 ls -l /home 的資料』而已!咦!原本的 ls -l / 資料就不見了嗎?是的! 因為該檔案的建立方式是:
1. 該檔案 (本例中是 ~/rootfile) 若不存在,系統會自動的將他建立起來,但是,
2. 當這個檔案存在的時候,那麼系統就會先將這個檔案內容清空,然後再將資料寫入!
3. 也就是若以 > 輸出到一個既存檔案中,呵呵,那個檔案就會被覆蓋掉囉!
那如果我想要將資料累加,不想要將舊的資料刪除,那該如何是好? 呵呵!就利用 >> 就好啦!例如上面的例子中,就變成『ls -l / >> ~/rootfile』 如此一來,當 ~/rootfile 不存在時,系統會主動建立這個檔案,若該檔案已存在, 則資料會在該檔案的最下方累加進去!基本上,指令的下達方式:
command > 1> 2> 2>> < 裝置或檔案
當然啦,一串指令的最左邊一定是指令,而在 >,2>,< 右邊的,必須是檔案或裝置才行! 此外,那個 > 會等於 1> ,因為 standard output 代碼是 1 ,可以省略啦! 再者, 1 與 > 之間並沒有空格喔!是緊接在一起的!注意注意!我們底下來玩幾個東西好了:
範例一:將目前目錄下的檔案資訊全部儲存到 list.txt 檔案中
[root@linux ~]# ls -al > list.txt
範例二:將根目錄下的資料也儲存到 list.txt 檔案中
[root@linux ~]# ls -al / >> list.txt
好了,對於『 > , >> 』這兩個東西有一定的概念之後,我們來深入的談一談『資料流重導向』的觀念吧! 如前所述,基本上, Linux 執行的結果中,可以約略的分成『正確輸出』與『錯誤輸出』兩種資料。 例如,當你以一般身份執行 find 這個指令時,例如執行『 find / -name testing 』時,由於你是一般身份,又有些資料夾是不允許一般身份者進入的, 所以囉,當你使用 find 時,就會有錯誤訊息發生了!但同時如果有 testing 這個檔案在你可以進入的資料夾當中,那麼螢幕也會輸出到給你看!因此, 就具有正確的與錯誤的輸出兩種囉!(分別稱為 Stdout 與 Stderror)例如下面為執行結果: 裡面的『 find: /home/root: Permission denied 』就告訴你該資料夾你沒有許可權進入, 這就是錯誤的輸出了,那麼『 /home/dmtsai/tseting 』就是正確的輸出了!
[dmtsai@linux ~]$ find /home -name testing
find: /home/test1: Permission denied <== Starndard error
find: /home/root: Permission denied <== Starndard error
find: /home/masda: Permission denied <== Starndard error
/home/dmtsai/testing <== Starndard output
好了,那麼假如我們想要將資料輸出到 list 這個檔案中呢?執行『 find / -name testing > list 』 會有什麼結果?呵呵,你會發現 list 裡面存了剛剛那個『正確』的輸出資料, 至於螢幕上還是會有錯誤的訊息出現呢!傷腦筋!如果想要將正確的與錯誤的資料分別存入不同的檔案中需要怎麼做?! 呵呵!其實在資料的重導向方面,正確的寫法應該是『 1> 』與『 2> 』才對!但是如果只有 > 則預設是以 1> 來進行資料的!那個 1> 是輸出正確資料, 2> 則是錯誤資料輸出項目。也就說:
1> :是將正確的資料輸出到指定的地方去
2> :是將錯誤的資料輸出到指定的地方去
好了,那麼上面的例子中,我們如何將資料輸出到不同的地方去呢?可以這麼寫:
[dmtsai@linux ~]$ find /home -name testing > list_right 2> list_error
這樣一來,剛剛執行的結果中,有 Permission 的那幾行錯誤資訊都會跑到 list_error 這個檔案中,至於正確的輸出資料則會存到 list_right 這個檔案中囉!這樣可以瞭解了嗎? 如果有點混亂的話,去休息一下再來看看吧!! 再來,如果我只要正確的資料,錯誤的資訊我不要了呢?呵呵,這個時候 /dev/null 這個垃圾桶就很重要了!/dev/null 是什麼呢? 基本上,那就有點像是一個『黑洞』的垃圾桶功能!當你輸入的任何東西導向到這個虛擬垃圾桶裝置時, 『他就會憑空消失不見了~~』,這個東西有用的很!例如上面的例子中,我們可以這麼做,來將錯誤的資訊丟掉!
[dmtsai@linux ~]$ find /home -name testing > list_right 2> /dev/null
很神奇呦! error message 就會『不見了!』呵呵!真高興!另外, 如果我要將資料都寫到同一個檔案中呢?這個時候寫法需要用到特殊寫法,請注意底下的寫法呦!
[dmtsai@linux ~]$ find /home -name testing > list 2> list <==錯誤寫法
[dmtsai@linux ~]$ find /home -name testing > list 2>&1 <==正確寫法
請特別留意這一點呢!同時寫入同一個檔案需要使用 2>&1 才對呦! OK!瞭解了 >, 2>, >> 與 /dev/null 之後,那麼那個 < 又是什麼呀!?呵呵!以最簡單的說法來說, 那就是『將原本需要由鍵盤輸入的資料,經由檔案來讀入』的意思。 舉例來說,我們可以使用 cat 在鍵盤上面輸入一些資料,然後寫入一個檔案內,例如:
[root@linux ~]# cat > catfile
testing
cat file test
<==這裡按下 [ctrl]+d 結束輸入來離開!
此時就會有 catfile 這個檔案產生,而且該檔案的內容就是剛剛輸入的內容喔。 那麼,我是否可以使用其它檔案來取代鍵盤輸入呢?可以啊!這樣做!
[root@linux ~]# cat > catfile < somefile
我可以先編輯 somefile ,然後再以上述的指令來將資料輸出到 catfile 去呢!這樣可以理解了嗎? 能夠理解 < 之後,再來則是怪可怕一把的 << 這個連續兩個小於的符號了~ 他代表的是『結束的輸入字元』的意思!舉例來講:『我要用 cat 直接將輸入的訊息輸出到 catfile 中, 且當輸入 eof 時,該次輸入就結束』,那我可以這樣做:
[root@linux ~]# cat > catfile <<eof
> This is a test testing
> OK now stop
> eof <==輸入這個玩意兒,嘿!立刻就結束了!
看到了嗎?利用 << 右側的控制字元,我們可以終止一次輸入, 而不必輸入 [crtl]+d 來結束哩!這對程式寫作很有協助喔!好了,那麼為何要使用命令輸出重導向呢? 這個問題一定會困擾你一下下的,如果你從來都沒有寫過 script 的話!好了,我們來說一說吧!
- 當螢幕輸出的資訊很重要,而且我們需要將他存下來的時候
- 背景執行中的程式,不希望他幹擾螢幕正常的輸出結果時;
- 一些系統的例行命令(例如寫在 /etc/crontab 中的檔案)的執行結果,希望他可以存下來時;
- 一些執行命令,我們已經知道他可能的錯誤訊息,所以想以『 2> /dev/null 』將他丟掉時;
- 錯誤訊息與正確訊息需要分別輸出時。
當然還有很多很多的功能的,最簡單的就是網友們常常問到的:『 為何我的 root 都會收到系統 crontab 寄來的錯誤訊息呢』這個咚咚是常見的錯誤, 而
如果我們已經知道這個錯誤訊息是可以忽略的時候,嗯!『 2> errorfile 』這個功能就很重要了吧! 瞭解了嗎??
命令執行的判斷依據: ; , &&, ||
在某些時候,我們希望可以一次執行多個指令,例如關機時,希望我可以先執行兩次 sync ,然後才 shutdown 電腦,那麼可以怎麼作呢?這樣做呀:
[root@linux ~]# sync; sync; shutdown -h now
在指令與指令中間利用分號 (;) 來隔開,這樣一來,分號前的指令執行完後, 就會立刻接著執行後面的指令了。這真是方便啊~再來,換個角度來想, 萬一我想要在某個目錄底下建立一個檔案,也就是說,如果該目錄存在的話, 那我才建立這個檔案,如果不存在,那就算了~目錄是否存在可以使用一些 bash 提供的判斷式功能, 但這裡假設我不曉得那個指令,但我知道我可以使用 ls 來判斷是否有該目錄的存在, 也就是說,我可以利用 ls directoryname 判斷是否存在,然後以 touch 建立一個檔案, 這兩個指令有相關性,那該如何寫呢?呵呵!可以利用 && 來作喔!
[root@linux ~]# ls /tmp && touch /tmp/testingagin
是否記得我們在變數的章節裡面談過這個奇怪的變數『 $? 』呢? 如果指令執行結果沒有錯誤訊息,那就會回傳 $?=0 ,如果有錯誤, 那回傳值就不會是 0 啊!經由這樣的判斷,我們也可以利用 && 來決定, 當前面的指令執行結果為正確 (例如:僅有 standard output 時),就可以接著執行後續的指令, 否則就予以略過!因此,當 ls /tmp 沒有問題,那麼就會接著執行 touch /tmp/testingagin 了! 萬一是這樣:
[root@linux ~]# ls /vbird && touch /vbird/test
因為我的系統裡面根本就不可能存在 /vbird 這個目錄呢!所以,執行 ls /vbird 就會回傳錯誤, 那麼後續的 touch /vbird/test 自然就不會動作囉!瞭解嗎? 再換個角度來想,如果我想要當某個檔案不存在時,就去建立那個檔案, 否則就略過呢?很簡單啊~可以這樣做:
[root@linux ~]# ls /tmp/vbirding || touch /tmp/vbirding
那個 || 剛好完全跟 && 相反,當前一個指令有錯誤時,在 || 後面的指令才會被執行! (要注意,那個 | 是兩個 | ,而 | 按鍵則是反斜線 \ 同一個按鍵, 因此,按下 [Shift] 加上 [\] 就會出現那個 | 囉!) 因此,簡單的來說,當 ls /tmp/vbirding 發生錯誤時,才會使用 touch /tmp/vbirding 去建立這個檔案的意思。 是否很有趣啊?這個 || 及 && 對於系統管理員在管理某些檔案許可權、存在等問題時, 可是很有用的東西喔!好了,現在我們來玩比較難一點的,看看底下的例題:
例題:以 ls 測試 /tmp/vbirding 是否存在,若存在則顯示 "exist" ,若不存在,則顯示 "not exist"!
這又牽涉到邏輯判斷的問題,如果存在就顯示某個資料,若不存在就顯示其它資料,那我可以這樣做:
ls /tmp/vbirding && echo "exist" || echo "not exist"
意思是說,當 ls /tmp/vbirding 執行後,若正確,就執行 echo "exist" ,若有問題,就執行 echo "not exist" !那如果我寫成:
ls /tmp/vbirding || echo "not exist" && echo "exist"
對不對啊?這其實是有問題的,為什麼呢?因為指令是一個一個往下執行,因此,在上面的例子當中,如果 /tmp/vbirding 不存在時,他會:
1. 若 ls /tmp/vbirding 不存在,因此回傳一個非為 0 的數值;
2. 接下來經過 || 的判斷,發現前一個指令回傳非為 0 的數值,因此,程式開始執行 echo "not exist" ,而 echo "not exist" 程式肯定可以執行成功,因此會回傳一個 0 值給後面的指令;
3. 經過 && 的判斷,咦!是 0 啊!所以就開始執行 echo "exist" 。
所以啊,嘿嘿!第二個例子裡面竟然會同時出現 not exist 與 exist 呢!真神奇~經過這個範例的練習,您應該會瞭解,由於指令是一個接著一個去執行的,因
此,如果真要使用判斷, 那麼這個 && 與 || 的順序就不能搞錯~一般來說,
判斷式最多會有三個,也就是:
command1 && command2 || command3
而且順序通常不會變,因為一般來說, command2 與 command3 會放置肯定可以執行成功的指令, 因此,依據上面例題的邏輯分析,您就會曉得為何要如此
放置囉~這很有用的啦!