終端機的環境設定: stty, set
什麼叫做『終端機環境』啊?!我們在 首次登入 Linux 時就提過,可以在 tty1 ~ tty6 這六個文字介面的終端機 (terminal) 環境中登入,那麼登入的時候我們可以取得一些字元設定的功能喔! 舉例來說,我們可以利用倒退鍵 (backspace,就是那個←符號的按鍵) 來刪除命令列上的字元, 也可以使用 [ctrl]+c 來強制終止一個指令的運行,當輸入錯誤時,就會有聲音跑出來警告。這是怎麼辦到的呢? 很簡單啊!因為登入終端機的時候,會自動的取得一些終端機的輸入環境的設定啊!
事實上,目前我們使用的 Linux distributions 都幫我們作了最棒的使用者環境了, 所以大家可以不用擔心操作環境的問題。不過,在某些 Unix like 的機器中,還是可能需要動用一些手腳, 才能夠讓我們的輸入比較快樂~舉例來說,利用 [backspace] 刪除,要比利用 [Del] 按鍵來的順手吧! 但是某些 Unix 偏偏是以 [del] 來進行字元的刪除啊!所以,這個時候就可以動動手腳囉~
那麼如何查閱目前的一些按鍵內容呢?可以利用 stty (setting tty 終端機的意思) 呢! stty 也可以幫助設定終端機的輸入按鍵代表意義喔!
[root@linux ~]# stty [-a]
參數:
-a :將目前所有的 stty 參數列出來;
範例:
範例一:列出所有的按鍵與按鍵內容
[root@linux ~]# stty -a
speed 38400 baud; rows 40; columns 80; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = ;
eol2 = ; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase
= ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl
ixon -ixoff -iuclc -ixany -imaxbel opost -olcuc -ocrnl onlcr -onocr
-onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0 isig icanon iexten
echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke
我們可以利用 stty -a 來列出目前環境中所有的按鍵列表,在上頭的列表當中,需要注意的是特殊字體那幾個, 此外,如果出現 ^ 表示 [Ctrl] 那個按鍵的意思。舉例來說, intr = ^C 表示利用 [ctrl] + c 來達成的。 幾個重要的代表意義是:
eof : End of file 的意思,代表『結束輸入』。
erase : 向後刪除字元,
intr : 送出一個 interrupt (中斷) 的訊號給目前正在 run 的程序;
kill : 刪除在目前指令列上的所有文字;
quit : 送出一個 quit 的訊號給目前正在 run 的程序;
start : 在某個程序停止後,重新啟動他的 output
stop : 停止目前螢幕的輸出;
susp : 送出一個 terminal stop 的訊號給正在 run 的程序。
記不記得我們講過 Linux 底下的幾個熱鍵 啊?沒錯! 就是這個 stty 設定值內的 intr / eof 囉~至於刪除字元,就是 erase 那個設定值啦! 如果你想要用 [ctrl]+h 來進行字元的刪除,那麼可以下達:
[root@linux ~]# stty erase ^h
那麼從此之後,你的刪除字元就得要使用 [ctrl]+h 囉,按下 [backspace] 則會出現 ^? 字樣呢! 如果想要回復利用 [backspace] ,就下達 stty erase ^? 即可啊! 至於更多的 stty 說明,記得參考一下 man stty 的內容喔!
除了 stty 之外,其實我們的 bash 還有自己的一些終端機設定值呢!那就是利用 set 來設定的! 我們之前提到一些變數時,可以利用 set 來顯示,除此之外,其實 set 還可以幫我們設定整個指令輸出/輸入的環境。 例如記錄歷史命令、顯示錯誤內容等等。
[root@linux ~]# set [-uvCHhmBx]
參數:
-u :預設不啟用。若啟用後,當使用未設定變數時,會顯示錯誤訊息;
-v :預設不啟用。若啟用後,在訊息被輸出前,會先顯示訊息的原始內容;
-x :預設不啟用。若啟用後,在指令被執行前,會顯示指令內容(前面有 ++ 符號)
-h :預設啟用。與歷史命令有關(下節介紹);
-H :預設啟用。與歷史命令有關(下節介紹);
-m :預設啟用。與工作管理有關(未來介紹);
-B :預設啟用。與刮號 [] 的作用有關;
-C :預設不啟用。若使用 > 等,則若檔案存在時,該檔案不會被覆蓋。
範例:
範例一:顯示目前所有的 set 設定值
[root@linux ~]# echo $-
himBH
# 那個 $- 變數內容就是 set 的所有設定啦! bash 預設是 himBH 喔!
範例二:設定 "若使用未定義變數時,則顯示錯誤訊息"
[root@linux ~]# set -u
[root@linux ~]# echo $vbirding
-bash: vbirding: unbound variable
# 預設情況下,未設定/未宣告 的變數都會是『空的』,不過,若設定 -u 參數,
# 那麼當使用未設定的變數時,就會有問題啦!很多的 shell 都預設啟用 -u 參數。
# 若要取消這個參數,輸入 set +u 即可!
範例三:執行前,顯示該指令內容。
[root@linux ~]# set -x
[root@linux ~]# echo $HOME
+ echo /root
/root
++ echo -ne '\033]0;root@linux:~\007'
# 看見否?要輸出的指令都會先被列印到螢幕上喔!前面會多出 + 的符號!
另外,其實我們還有其他的按鍵設定功能呢!就是在 /etc/inputrc 這個檔案裡面設定。
[root@linux ~]# cat /etc/inputrc
# do not bell on tab-completion
#set bell-style none
set meta-flag on
set input-meta on
set convert-meta off
set output-meta on
.....以下省略.....
還有例如 /etc/DIR_COLORS* 與 /etc/termcap 等,也都是與終端機有關的環境設定檔案呢! 不過,事實上,鳥哥並不建議您修改 tty 的環境呢,這是因為 bash 的環境已經設定的很親和了, 我們不需要額外的設定或者修改,否則反而會產生一些困擾。不過,寫在這裡的資料, 只是希望大家能夠清楚的知道我們的終端機是如何進行設定的喔! ^_^
--------------------------------------------------------------------------------
萬用字元與特殊符號:
嘿嘿!在 bash 裡頭還支援一些萬用字元喔 (wild card) !多了這些萬用字元, 我們利用 bash 處理資料就更方便了!底下我們列出一些常用的萬用字元喔:
符號 內容
* 萬用字元,代表 0 個或多個字元(或數字)
? 萬用字元,代表『一定有』一個字母
# 註解,這個最常被使用在 script 當中,視為說明!
\ 跳脫符號,將『特殊字元或萬用字元』還原成一般字元
| 分隔兩個管線命令的界定;
; 連續性命令的界定(注意!與管線命令並不相同)
~ 使用者的家目錄
$ 亦即是變數之前需要加的變數取代值
& 將指令變成背景下工作
! 邏輯運算意義上的『非』 not 的意思!
/ 路徑分隔的符號
>, >> 輸出導向,分別是『取代』與『累加』
' 單引號,不具有變數置換的功能
" 具有變數置換的功能!
` ` 兩個『 ` 』中間為可以先執行的指令!
( ) 在中間為子 shell 的起始與結束
[ ] 在中間為字元的組合
{ } 在中間為命令區塊的組合!
組合按鍵 執行結果
Ctrl + C 終止目前的命令
Ctrl + D 輸入結束(EOF),例如郵件結束的時候;
Ctrl + M 就是 Enter 啦!
Ctrl + S 暫停螢幕的輸出
Ctrl + Q 恢復螢幕的輸出
Ctrl + U 在提示字元下,將整列命令刪除
Ctrl + Z 『暫停』目前的命令
在上面的『按鍵組合』當中,有沒有發現跟上個小節很相似的內容啊!? 呵呵~沒錯啦!那些組合鍵都可以在 stty 當中來進行不同的設定的!好玩吧! 至於上面的萬用字元當中,最常用的就屬 *, ?, [] 及 ` 了!我們提幾個簡單的例子:
[root@linux ~]# ls test* <==那個 * 代表後面不論接幾個字元都予以接受
[root@linux ~]# ls test? <==那個 ? 代表後面『一定』要接『一個』字元
[root@linux ~]# ls test??? <==那個 ??? 代表『一定要接三個』字元!
[root@linux ~]# cp test[1-5] /tmp
# 將 test1, test2, test3, test4, test5 若存在的話,就拷貝到 /tmp
[root@linux ~]# cp test[!1-5] /tmp
# 只要不是 test1, test2, test3, test4, test5 之外的其他 test? ,
# 若存在的話,就拷貝到 /tmp
[root@linux ~]# cd /lib/modules/`uname -r`/kernel/drivers
# 被 ` ` 括起來的內容『會先執行』
上面幾個例子相當的有趣!尤其是最後面兩個!需要注意的是, [1-5] 裡面『代表只有一個字元』但是範圍可以由 1-5 ,這樣來說的話,那麼我們如果允許『只要檔名裡面含有至少一個大寫字元』時,就可以將檔案 copy 出來的話,可以這樣做:
cp *[A-Z]* /tmp
很有趣吧?!也就是說『 [ ] 謹代表一個字元,而這個字元的定義可以是範圍(-), 可以是指定項目,也可以是兩者並存。 』舉例來說,我想要找出在 /etc/ 底下所有含有數字的檔案, 可以這樣:
ls -lda /etc/*[0-9]*
但如果我只想要找出含有 3 及 5 的檔名的檔案呢?就會是這樣:
ls -lda /etc/*[35]*
如果是『不想要』某些範圍或者是單字呢?就使用 [!] 即可!例如不想要有小寫字元為開頭的檔案:
ls -lda /etc/[!a-z]*
很好玩吧!至於那個 ` 是啥?在一串指令當中, `command` 內的指令會先被執行, 執行完的訊息再回傳到外部指令來處理!也就是說:
系統先執行 uname -r 找出輸出的結果;
將結果累加在目錄上面,來執行 cd 的功能!
很棒吧!!另外,這個 quot (`) 的功能,也可以利用 $() 來取代喔!例如:
cd /lib/modules/$(uname -r)/kernel
這些基本的功能需要特別來瞭解一下才行呦!至於更多的使用方式, 我們會在後續的正規表示法當中在詳談的!
--------------------------------------------------------------------------------
資料流重導向
資料流重導向 (redirect) 由字面上的意思來看,好像就是將『資料給他傳導到其他地方去』的樣子? 呵呵!是啊是啊!沒錯~資料流重導向就是將某個指令執行後應該要出現在螢幕上的資料, 給他傳輸到其他的地方,例如檔案或者是裝置 (例如印表機之類的!)!這玩意兒在 Linux 的文字模式底下可重要的! 尤其是如果我們想要將某些資料儲存下來時,就更有用了!
--------------------------------------------------------------------------------
什麼是資料流重導向
好傢伙!什麼是資料流重導向啊?這得要由指令的執行結果談起! 一般來說,如果你要執行一個指令,通常他會是這樣的:
圖三、指令執行過程的資料傳輸情況
我們執行一個指令的時候,這個指令可能會由檔案讀入資料,經過處理之後,再將資料輸出到螢幕上。 在圖三當中, standard output 與 standard error 分別代表標準輸出與標準錯誤輸出, 這兩個玩意兒預設都是輸出到螢幕上面來的啊!舉個簡單例子來說, 我們下達『 cat /etc/crontab /etc/vbirdsay 』這個指令時,cat 會由 /etc/crontab 與 /etc/vbirdsay 讀入資料, 然後再將資料輸出到螢幕上,不過,因為系統本來就不存在 /etc/vbirdsay 這個檔案, 所以就會顯示錯誤訊息,這個錯誤訊息也會輸出到螢幕上來喔!
在這樣的過程當中,我們可以將 standard error (簡稱 stderr) 與 standard output (簡稱 stdout) 給他傳送到其他不同的地方,而不是螢幕上頭!傳送的目標處,通常是檔案或者是裝置! 而傳送的指令則是如下所示:
標準輸入(stdin) :代碼為 0 ,使用 < 或 << ;
標準輸出(stdout):代碼為 1 ,使用 > 或 >> ;
標準錯誤輸出(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 / 資料就不見了嗎?是的! 因為該檔案的建立方式是:
該檔案 (本例中是 ~/rootfile) 若不存在,系統會自動的將他建立起來,但是,
當這個檔案存在的時候,那麼系統就會先將這個檔案內容清空,然後再將資料寫入!
也就是若以 > 輸出到一個既存檔案中,呵呵,那個檔案就會被覆蓋掉囉!
那如果我想要將資料累加,不想要將舊的資料刪除,那該如何是好? 呵呵!就利用 >> 就好啦!例如上面的例子中,就變成『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 <> 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 不存在時,他會:
若 ls /tmp/vbirding 不存在,因此回傳一個非為 0 的數值;
接下來經過 || 的判斷,發現前一個指令回傳非為 0 的數值,因此,程式開始執行 echo "not exist" ,而 echo "not exist" 程式肯定可以執行成功,因此會回傳一個 0 值給後面的指令;
經過 && 的判斷,咦!是 0 啊!所以就開始執行 echo "exist" 。
所以啊,嘿嘿!第二個例子裡面竟然會同時出現 not exist 與 exist 呢!真神奇~
經過這個範例的練習,您應該會瞭解,由於指令是一個接著一個去執行的,因此,如果真要使用判斷, 那麼這個 && 與 || 的順序就不能搞錯~一般來說,判斷式最多會有三個,也就是:
command1 && command2 || command3
而且順序通常不會變,因為一般來說, command2 與 command3 會放置肯定可以執行成功的指令, 因此,依據上面例題的邏輯分析,您就會曉得為何要如此放置囉~這很有用的啦! 而且.....考試也很常考~
--------------------------------------------------------------------------------
管線命令 (pipe)
就如同前面所說的, bash 命令執行的時候有輸出的資料會出現! 那麼如果這群資料必需要經過幾道手續之後才能得到我們所想要的格式,應該如何來設定? 這就牽涉到管線命令的問題了 (pipe) ,管線命令使用的是『 | 』這個界定符號! 另外,管線命令與『連續下達命令』是不一樣的呦! 這點底下我們會再說明。底下我們先舉一個例子來說明一下簡單的管線命令。
假設我們想要知道 /etc/ 底下有多少檔案,那麼可以利用 ls /etc 來查閱,不過, 因為 /etc 底下的檔案太多,導致一口氣就將螢幕塞滿了~不知道前面輸出的內容是啥?此時,我們可以透過 less 指令的協助,利用:
[root@linux ~]# ls -al /etc | less
嘿嘿!如此一來,使用 ls 指令輸出後的內容,就能夠被 less 讀取, 並且利用 less 的功能,我們就能夠前後翻動相關的資訊了!很方便是吧?呵呵! 我們就來瞭解一下這個管線命令『 | 』的用途吧!
這個管線命令『 | 』僅能處理經由前面一個指令傳來的正確資訊,也就是 standard output ( STDOUT ) 的資訊,對於 stdandard error 並沒有直接處理的能力,請記得。那麼整體的管線命令可以使用下圖表示之:
圖四、管線命令的處理示意圖
在每個管線的前後部分都是『指令』呢!而後一個指令的輸入乃是由前一個指令的輸出而來的! 不過,要注意的是,在 Linux 的環境中,很多的訊息處理都是以『行』為單位~ 也就是以是否具有 [Enter] 標誌 (CR) 來作為一段處理的依據喔! 底下我們來談一談一些基本的管線命令指令介紹:
--------------------------------------------------------------------------------
擷取命令: cut, grep
什麼是擷取命令啊?說穿了,就是將一段資料經過分析後,取出我們所想要的。 或者是,經由分析關鍵字,取得我們所想要的那一行! 不過,要注意的是,一般來說,擷取訊息通常是針對『一行一行』來分析的, 並不是整篇訊息分析的喔~底下我們介紹兩個很常用的訊息擷取命令:
--------------------------------------------------------------------------------
cut
cut 不就是『切』嗎?沒錯啦!這個指令可以將一段訊息的某一段給他『切』出來~ 處理的訊息是以『行』為單位喔!底下我們就來談一談:
[root@linux ~]# cut -d'分隔字元' -f fields
[root@linux ~]# cut -c 字元區間
參數:
-d :後面接分隔字元。與 -f 一起使用;
-f :依據 -d 的分隔字元將一段訊息分割成為數段,用 -f 取出第幾段的意思;
-c :以字元 (characters) 的單位取出固定字元區間;
範例:
範例一:將 PATH 變數取出,我要找出第五個路徑。
[root@linux ~]# echo $PATH
/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/X11R6/bin:/usr/games:
[root@linux ~]# echo $PATH | cut -d ':' -f 5
# 嘿嘿!如此一來,就會出現 /usr/local/bin 這個目錄名稱!
# 因為我們是以 : 作為分隔符號,第五個就是 /usr/local/bin 啊!
# 那麼如果想要列出第 3 與第 5 呢?,就是這樣:
[root@linux ~]# echo $PATH | cut -d ':' -f 3,5
範例二:將 export 輸出的訊息,取得第 12 字元以後的所有字串
[root@linux ~]# export
declare -x HISTSIZE="1000"
declare -x INPUTRC="/etc/inputrc"
declare -x KDEDIR="/usr"
declare -x LANG="zh_TW.big5"
......其他省略......
[root@linux ~]# export | cut -c 12-
HISTSIZE="1000"
INPUTRC="/etc/inputrc"
KDEDIR="/usr"
LANG="zh_TW.big5"
......其他省略......
# 知道怎麼回事了吧?用 -c 可以處理比較具有格式的輸出資料!
# 我們還可以指定某個範圍的值,例如第 12-20 的字元,就是 cut -c 12-20 等等!
範例三:用 last 將這個月登入者的資訊中,僅留下使用者大名
[root@linux ~]# last
vbird tty1 192.168.1.28 Mon Aug 15 11:55 - 17:48 (05:53)
vbird tty1 192.168.1.28 Mon Aug 15 10:17 - 11:54 (01:37)
[root@linux ~]# last | cut -d ' ' -f 1
# 用 last 可以取得最近一個月登入主機的使用者資訊,
# 而我們可以利用空白字元的間隔,取出第一個資訊,就是使用者帳號囉!
# 但是因為 vbird tty1 之間空格有好幾個,並非僅有一個,所以,如果要找出
# tty1 其實不能以 cut -d ' ' -f 1,2 喔!輸出的結果會不是我們想要的。
這個 cut 實在很好用!不過,說真的,除非你常常在分析 log 檔案,否則使用到 cut 的機會並不多!好了! cut 主要的用途在於將『同一行裡面的資料進行分解!』, 最常使用在分析一些數據或文字資料的時候!這是因為有時候我們會以某些字元當作分割的參數, 然後來將資料加以切割,以取得我們所需要的資料。我也很常使用這個功能呢!尤其是在分析 log 檔案的時候!不過, cut 在處理多空格相連的資料時,可能會比較吃力一點~
--------------------------------------------------------------------------------
grep
剛剛的 cut 是將一行訊息當中,取出某部分我們想要的,而 grep 則是分析一行訊息, 若當中有我們所需要的資訊,就將該行拿出來~簡單的語法是這樣的:
[root@linux ~]# grep [-acinv] '搜尋字串' filename
參數:
-a :將 binary 檔案以 text 檔案的方式搜尋資料
-c :計算找到 '搜尋字串' 的次數
-i :忽略大小寫的不同,所以大小寫視為相同
-n :順便輸出行號
-v :反向選擇,亦即顯示出沒有 '搜尋字串' 內容的那一行!
範例:
範例一:將 last 當中,有出現 root 的那一行就取出來;
[root@linux ~]# last | grep 'root'
範例二:與範例一相反,只要沒有 root 的就取出!
[root@linux ~]# last | grep -v 'root'
範例三:在 last 的輸出訊息中,只要有 root 就取出,並且僅取第一欄
[root@linux ~]# last | grep 'root' |cut -d ' ' -f1
# 在取出 root 之後,利用上個指令 cut 的處理,就能夠僅取得第一欄囉!
grep 是個很棒的指令喔!他支援的語法實在是太多了~用在正規表示法裡頭, 能夠處理的資料實在是多的很~不過,我們這裡先不談正規表示法~下一章再來說明~ 您先瞭解一下, grep 可以解析一行文字,取得關鍵字,若該行有存在關鍵字, 就會整行列出來!
--------------------------------------------------------------------------------
排序命令: sort, wc, uniq
很多時候,我們都會去計算一次資料裡頭的相同型態的資料總數,舉例來說, 使用 last 可以查得這個月份有登入主機者的身份。那麼我可以針對每個使用者查出他們的總登入次數嗎? 此時就得要排序與計算之類的指令來輔助了!底下我們介紹幾個好用的排序與統計指令喔!
--------------------------------------------------------------------------------
sort
sort 是很有趣的指令,他可以幫我們進行排序,而且可以依據不同的資料型態來排序喔! 例如數字與文字的排序就不一樣。此外,排序的字元與語系的編碼有關,因此, 如果您需要排序時,建議使用 LC_ALL=C 來讓語系統一,資料排序比較好一些。
[root@linux ~]# sort [-fbMnrtuk] [file or stdin]
參數:
-f :忽略大小寫的差異,例如 A 與 a 視為編碼相同;
-b :忽略最前面的空白字元部分;
-M :以月份的名字來排序,例如 JAN, DEC 等等的排序方法;
-n :使用『純數字』進行排序(預設是以文字型態來排序的);
-r :反向排序;
-u :就是 uniq ,相同的資料中,僅出現一行代表;
-t :分隔符號,預設是 tab 鍵;
-k :以那個區間 (field) 來進行排序的意思,
範例:
範例一:個人帳號都記錄在 /etc/passwd 下,請將帳號進行排序。
[root@linux ~]# cat /etc/passwd | sort
adm:x:3:4:adm:/var/adm:/sbin/nologin
apache:x:48:48:Apache:/var/www:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
# 我省略很多的輸出~由上面的資料看起來, sort 是預設『以第一個』資料來排序,
# 而且預設是以『文字』型態來排序的喔!所以由 a 開始排到最後囉!
範例二:/etc/passwd 內容是以 : 來分隔的,我想以第三欄來排序,該如何?
[root@linux ~]# cat /etc/passwd | sort -t ':' -k 3
root:x:0:0:root:/root:/bin/bash
iiimd:x:100:101:IIIMF server:/usr/lib/iiim:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
# 看到特殊字體的輸出部分了吧?怎麼會這樣排列啊?呵呵!沒錯啦~
# 如果是以文字型態來排序的話,原本就會是這樣,想要使用數字排序:
# cat /etc/passwd | sort -t ':' -k 3 -n
# 這樣才行啊!用那個 -n 來告知 sort 以數字來排序啊!
範例三:利用 last ,將輸出的資料僅取帳號,並加以排序
[root@linux ~]# last | cut -d ' ' -f1 | sort
sort 同樣是很常用的指令呢!因為我們常常需要比較一些資訊啦! 舉個上面的第二個例子來說好了!今天假設你有很多的帳號,而且你想要知道最大的使用者 ID 目前到哪一號了!呵呵!使用 sort 一下子就可以知道答案咯!當然其使用還不止此啦! 有空的話不妨玩一玩!
--------------------------------------------------------------------------------
uniq
如果我排序完成了,想要將重複的資料僅列出一個顯示,可以怎麼做呢?
[root@linux ~]# uniq [-ic]
參數:
-i :忽略大小寫字元的不同;
-c :進行計數
範例:
範例一:使用 last 將帳號列出,僅取出帳號欄,進行排序後僅取出一位;
[root@linux ~]# last | cut -d ' ' -f1 | sort | uniq
範例二:承上題,如果我還想要知道每個人的登入總次數呢?
[root@linux ~]# last | cut -d ' ' -f1 | sort | uniq -c
這個指令用來將『重複的行刪除掉只顯示一個』,舉個例子來說, 你要知道這個月份登入你主機的使用者有誰,而不在乎他的登入次數,那麼就使用上面的範例, (1)先將所有的資料列出;(2)再將人名獨立出來;(3)經過排序;(4)只顯示一個! 由於這個指令是在將重複的東西減少,所以當然需要『配合排序過的檔案』來處理囉!
--------------------------------------------------------------------------------
wc
如果我想要知道 /etc/man.config 這個檔案裡面有多少字?多少行?多少字元的話, 可以怎麼做呢?其實可以利用 wc 這個指令來達成喔!他可以幫我們計算輸出的訊息的整體資料!
[root@linux ~]# wc [-lwm]
參數:
-l :僅列出行;
-w :僅列出多少字(英文單字);
-m :多少字元;
範例:
範例一:那個 /etc/man.config 裡面到底有多少相關字、行、字元數?
[root@linux ~]# cat /etc/man.config | wc
138 709 4506
# 輸出的三個數字中,分別代表: 『行、字數、字元數』
範例二:我知道使用 last 可以輸出登入者,但是 last 最後兩行並非帳號內容,
那麼請問,我該如何以一行指令串取得這個月份登入系統的總人次?
[root@linux ~]# last | grep [a-zA-Z] | grep -v 'wtmp' | wc -l
# 由於 last 會輸出空白行與 wtmp 字樣在最底下兩行,因此,我利用
# grep 取出非空白行,以及去除 wtmp 那一行,在計算行數,就能夠瞭解囉!
wc 也可以當作指令?呵呵!這可不是上洗手間的 WC 呢! 這是相當有用的計算檔案內容的一個工具組喔!舉個例子來說, 當你要知道目前你的帳號檔案中有多少個帳號時,就使用這個方法:『 cat /etc/passwd | wc -l 』啦!因為 /etc/passwd 裡頭一行代表一個使用者呀! 所以知道行數就曉得有多少的帳號在裡頭了!而如果要計算一個檔案裡頭有多少個字元時,呵呵!就使用 wc -c 這個參數吧!