(轉)鳥哥SHELL入門材料

http://blog.chinaunix.net/space.php?uid=9809038&do=blog&cuid=62903php

 

 

經典入門材料!node

學習 Shell Scripts

若是您真的很想要走信息這條路,而且想要好好的管理好屬於您的主機,那麼,別說鳥哥不告訴您, Shell Scripts 真的是必需要學習的一項課題呢!基本上, shell script 有點像是早期的批次檔, 亦便是將一些指令彙整起來一次執行,可是 Shell script 擁有更強大的功能,那就是, 他能夠進行相似程序 (program) 的撰寫,而且,不須要通過編譯 (compiler) 就可以執行, 真的很方便。加上,咱們可透過 shell script 來簡化咱們平常的工做管理, 並且,整個 Linux 環境中,一些服務 (services) 的啓動都是透過 shell script 的, 若是您對於 script 不瞭解,嘿嘿!發生問題時,可真是會求助無門喔! 因此,好好的學一學他吧!  


1. 什麼是 Shell Script
  1.1 幹嗎學習 shell scripts?
  1.2 第一支 script 的撰寫與執行
  1.3 撰寫 shell script 的良好習慣創建
2. 簡單的 shell script 練習:
3. 善用判斷式:
  3.1 利用 test 指令的測試功能
  3.2 利用判斷符號 [ ]
  3.3 Shell script 的預設變數($0, $1...)
4. 條件判斷式:
  4.1 利用 if .... then
  4.2 利用 case ..... esac 判斷
  4.3 利用 function 功能
5. 循環 (loop)
  5.1 while....do....done, until....do....done
  5.2 for...do...done
6. shell script 的追蹤與 debug
7. 本章習題練習


--------------------------------------------------------------------------------
什麼是 Shell scripts ?

這個有趣的問題趕忙來回答看看,什麼是 shell script 呢? shell 咱們在 認識 bash 當中已經提過了,那是一個文字接口底下讓咱們與系統溝通的一個工具接口,那麼 script 是啥? 字面上的意義, script 是『腳本、劇本』的意思。整句話是說, shell script 是針對 shell 所寫的『劇本!』 什麼東西啊?呵呵!其實, shell script 是利用 shell 的功能所寫的一個『程序 (program)』,這個程序是使用純文字文件,將一些 shell 的語法與指令寫在裏面, 搭配正規表示法、管線命令與數據流重導向等功能,以達到咱們所想要的處理目的。

因此,簡單的說, shell script 就像是早期 DOS 年代的批次檔 (.bat) ,最簡單的功能就是將許多指令彙整寫在一塊兒, 讓使用者很輕易的就可以 one touch (執行一個檔案 "shell script" ,就可以一次執行多個指令), 而, shell script 更提供數組、循環、條件與邏輯判斷等重要功能,讓使用者也能夠直接以 shell 來撰寫程序,而沒必要使用相似 C 程序語言等傳統程序撰寫的語法呢!

那,這麼說您能夠了解了嗎?是的! shell script 能夠簡單的被當作是批次檔, 也能夠被說成是一個程序語言,且這個程序語言因爲都是利用 shell 與相關工具指令, 因此不須要編譯便可執行,且擁有不錯的除錯 (debug) 工具,因此,他能夠幫助系統管理員快速的管理好主機。


--------------------------------------------------------------------------------
幹嗎學習 shell scripts?

這是個好問題,我又幹嗎必定要學 shell script ?我又不是信息人,沒有寫程序的概念, 那我幹嗎還要學 shell script 呢?不要學可不能夠啊?呵呵~若是 Linux 對您而言, 您只是想要『會用』而已,那麼,不須要學 shell script 也還無所謂,這部分先給他跳過去, 等到有空的時候,再來好好的瞧一瞧。可是,若是您是真的想要玩清楚 Linux 的前因後果, 那麼 shell script 就不可不知,爲何呢?由於:


自動化管理的重要依據: 
不用鳥哥說您也知道,管理一部主機真不是件簡單的事情,天天要進行的任務就有: 查詢登陸檔、追蹤流量、監控使用者使用主機狀態、主機各項硬設備狀態、 主機軟件更新查詢、更不要說得應付其它使用者的忽然要求了。而這些工做, 您想要自行手動處理,仍是寫個簡單的程序來幫您每日自動處理分析,如有問題才通知您呢? 固然是讓系統自動工做比較好,對吧!呵呵~這就得要良好的 shell script 來幫忙的啦!


追蹤與管理系統的重要工做: 
雖然咱們尚未提到服務啓動的方法,不過,這裏能夠先提一下,咱們 Linux 系統的服務 ( services ) 啓動的接口,在 /etc/init.d/ 這個目錄下,全部的檔案都是 scripts ; 另外,包括開機 (booting) 過程也都是利用 shell script 來幫忙搜尋系統的相關設定數據, 而後再代入各個服務的設定參數啊!舉例來講,若是咱們想要從新啓動系統登陸文件, 可使用:『/etc/init.d/syslogd restart』,那個 syslogd 檔案就是 script 啦! 另外,我曾經在某一代的 FC 上面發現,啓動 MySQL 這個數據庫服務時,確實是能夠啓動的, 可是屏幕上卻總是出現『failure』,後來才發現,原來是啓動 MySQL 那個 script 會主動的以『空的密碼』去嘗試登入 MySQL ,但我修改過 MySQL 的密碼囉~固然就登入失敗~ 後來改了改 script ,就略去這個問題啦!如此說來, script 確實是須要學習的啊!


簡單入侵偵測功能: 
當咱們的系統有異狀時,大多會將這些異狀記錄在系統記錄器,也就是咱們常提到的『系統登陸文件』, 那麼咱們能夠在固定的幾分鐘內主動的去分析系統登陸文件,若察覺有問題,就馬上通報管理員, 或者是馬上增強防火牆的設定規則,如此一來,您的主機可就可以達到『自我保護』的聰明學習功能啦~ 舉例來講,咱們能夠經過 shell script 去分析『當該封包嘗試幾回仍是聯機失敗以後,就予以抵擋住該 IP』之類的舉動,例如鳥哥寫過一個關於抵擋砍站軟件的 shell script , 就是用這個想法去達成的呢!


連續指令單一化: 
其實,對於新手而言, script 最簡單的功能就是:『彙整一些在 command line 下達的連續指令,將他寫入 scripts 當中,而由直接執行 scripts 來啓動一連串的 command line 指令輸出入!』例如: 防火牆連續規則 ( iptables ),開機加載程序的項目 ( 就是在 /etc/rc.d/rc.local 裏頭的數據 ) ,等等都是類似的功能啦! 其實,說穿了,若是不考慮 program 的部分,那麼 scripts 也能夠想成,僅是幫咱們把一大串的指令彙整在一個檔案裏面, 而直接執行該檔案就能夠執行那一串又臭又長的指令段!就是這麼簡單啦!


簡易的數據處理: 
由前一章 正規表示法 的 awk 程序說明中, 您能夠發現, awk 能夠用來處理簡單的數據數據呢!例如薪資單的處理啊等等的。 shell script 的功能更強大,例如鳥哥曾經用 shell script 直接處理數據數據的比對啊, 文字數據的處理啊等等的,撰寫方便,速度又快(由於在 Linux 效能較佳), 真的是很不錯用的啦!


跨平臺支持與學習歷程較短: 
幾乎全部的 Unix Like 上面均可以跑 shell script ,連 MS Windows 系列也有相關的仿真器能夠用, 此外, shell script 的語法是至關親和的,看都看的懂得文字,而不是機器碼, 很容易學習~這些都是您能夠加以考慮的學習點啊!


上面這些都是您考慮學習 shell script 的特色~此外, shell script 還能夠簡單的以 vi 來直接編寫,實在是很方便的好東西!因此,仍是建議您學習一下啦。

不過,雖然 shell script 號稱是程序 (program) ,但實際上, shell script 處理數據的速度上是不太夠的。由於 shell script 用的是外部的指令與 bash shell 的一些預設工具,因此,他經常會去呼叫外部的函式庫,所以,運算速度上面固然比不上傳統的程序語言。 因此囉, shell script 用在系統管理上面是很好的一項工具,可是用在處理大量數值運算上, 就不夠好了~並且還很麻煩,由於:Shell scripts 的速度較慢, 且使用的 CPU 資源較多,形成主機資源的分配不良。還好, 咱們確實不多看到利用 shell script 在進行大量數據運算的,因此,沒必要擔憂的啦!



--------------------------------------------------------------------------------
第一支 script 的撰寫與執行

如同前面講到的, shell script 其實就是純文字文件 (ASCII) ,咱們能夠編輯這個檔案, 而後讓這個檔案來幫咱們一次執行多個指令,或者是利用一些運算與邏輯判斷來幫咱們達成某些功能。 因此啦,要編輯這個檔案的內容時,固然就須要具有有 bash shell 指令下達的相關認識。 咱們說過,要下達指令須要注意的事項在 bash 章節內已經提過, 在 shell script 的撰寫一樣須要用到這些注意事項的:
如同前面 bash command 提到的,指令與參數間的多個空白會被忽略掉; 
而空白行也將被忽略掉!,而且 [tab] 也是不會被理會的! 
若是讀取到一個 Enter 符號 ( CR )),就嘗試開始執行該行命令; 
至於若是一行的內容太多,則可使用 [Enter] 來延伸至下一行; 
此外,使用最多的 # 可作爲批註!任何加在 # 後面的字,將所有被視爲批註文字而被忽略! 
如此一來,咱們在 script 內所撰寫的程序,就會被一行一行的執行。好了,那麼這個程序假設文件名是 shell.sh 好了,如何執行這個檔案?很簡單,能夠有底下幾個方法:
將 shell.sh 加上可讀與執行 (rx) 的權限,而後就可以以 ./shell.sh 來執行了; 
直接以 sh shell.sh 的方式來直接執行便可。
反正重點就是要讓那個 shell.sh 內的指令能夠被執行的意思啦!咦!那我爲什麼須要使用 ./shell.sh 來下達指令? 還記得咱們在 bash 裏面一直強調的,指令是否可以被執行與 PATH 這個環境變量有關, 因此,要執行『目前這個目錄下的某個檔案』就須要加上 ./ 這個目錄啦!另外,其實您也能夠將 shell.sh 放在您家目錄下的 ~/bin 這個目錄中,而後利用 PATH="$PATH":~/bin 的設定, 嘿嘿,就可以直接執行您的 script 囉~ ^_^

那,爲什麼 sh shell.sh 也能夠執行呢?這是由於 /bin/sh 其實就是 /bin/bash , 使用 sh shell.sh 亦即告訴系統,我想要直接以 bash 的功能來執行 shell.sh 這個檔案內的相關指令的意思。 而咱們也能夠利用 sh 的參數,如 -n 及 -x 來檢查與追蹤 shell.sh 的語法是否正確呢! ^_^


--------------------------------------------------------------------------------

撰寫第一支 script 
不管是那個門派,要學武功要從掃地作起,那麼要學程序呢?呵呵,確定是由『秀出 Hello World!』 這個字眼開始的!OK!那麼鳥哥就先寫一支 script 給你們瞧一瞧:
[root@linux ~]# mkdir scripts; cd scripts
[root@linux scripts]# vi sh01.sh
#!/bin/bash
# Program:
#       This program is used to show "Hello World !" in screen.
# History:
# 2005/08/23        VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e "Hello World ! a "
exit 0


在咱們這個章節當中,請將全部的撰寫的 script 放置到您家目錄的 ~/scripts 這個目錄內, 比較好管理啦!上面的寫法當中,我主要將整個程序的撰寫分紅數段,大體是這樣:
第一行 #!/bin/bash 在宣告這個 script 使用的 shell 名稱:
由於咱們使用的是 bash ,因此,必需要以『 #!/bin/bash 』來宣告這個檔案內的語法使用 bash 的語法!那麼當這個程序被執行時,他就可以加載 bash 的相關環境設定檔, 而且執行 bash 來使咱們底下的指令可以執行!這很重要的!(在不少情況中,若是沒有設定好這一行, 那麼該程序極可能會沒法執行,由於系統可能沒法判斷該程序須要使用什麼 shell 來執行啊!)


程序內容的宣告:
整個 script 當中,除了第一行的 #! 是用來宣告 shell 的以外,其它的 # 都是『批註』用途! 因此上面的程序當中,第二行如下就是用來講明整個程序的狀態。通常來講, 建議您必定要養成說明該 script 的:1. 內容與功能; 2. 版本信息; 3. 做者與聯絡方式; 4. 建檔日期;5. 歷史紀錄 等等。這將有助於將來程序的改寫與 debug 呢!


主要環境變量的宣告:
建議務必要將一些重要的環境變量設定好,鳥哥我的認爲, PATH 是當中最重要的! 如此一來,則可以讓咱們這支程序在進行時,能夠直接下達指令, 而沒必要寫絕對路徑呢!比較好啦!


主要程序部分
就將主要的程序寫好便可!在這個例子當中,就是 echo 那一行啦!


執行成果告知
是否記得咱們在 bash 裏面要討論一個指令的執行成功與否,可使用 $? 這個變量來觀察~ 那麼咱們也能夠利用 exit 這個指令來讓程序中斷,而且回傳一個數值給系統。 在咱們這個例子當中,我使用 exit 0 ,這表明離開 script ,而且回傳一個 0 給系統, 因此我執行完這個 script 後,若接着下達 echo $? 則可獲得 0 的值喔! 更聰明的讀者應該也知道了,呵呵!利用這個 exit n 的功能,咱們還能夠自訂錯誤訊息, 讓這支程序變得更加的 smart 呢! 
接下來執行看看結果是怎樣吧?
[root@linux scripts]# sh sh01.sh
Hello World !



您會看到屏幕是這樣,並且應該還會聽到『咚』的一聲,爲何呢?還記得前一章提到的 printf 吧?用 echo 接着那些特殊的按鍵也能夠發生一樣的事情~ 不過, echo 必需要加上 -e 的參數才行! 呵呵!在您寫完這個小 script 以後,您就能夠大聲的說:『我也會寫程序了』!哈哈! 很簡單有趣吧~ ^_^

另外,你也能夠利用:『chmod a+x sh01.sh; ./sh01.sh』來執行這個 script 的呢!


--------------------------------------------------------------------------------
撰寫 shell script 的良好習慣創建

一個良好習慣的養成是很重要的~你們在剛開始撰寫程序的時候,最容易忽略這部分, 認爲程序寫出來就行了,其它的不重要。其實,若是程序的說明可以更清楚, 那麼對您本身是有很大的幫助的。

舉例來講,鳥哥本身爲了本身的需求,曾經撰寫了很多的 script 來幫我進行主機 IP 的偵測啊、 登陸檔分析與管理啊、自動上傳下載重要設定檔啊等等的,不過,早期就是由於太懶了, 管理的主機又太多了,經常同一個程序在不一樣的主機上面進行更改,到最後,到底哪一支纔是最新的都記不起來, 並且,重點是,我究竟是改了哪裏??爲何作那樣的修改?都忘的一乾二淨~真要命~

因此,後來鳥哥在寫程序的時候,一般會比較仔細的將程序的設計過程給他記錄下來, 並且還會記錄一些歷史紀錄,如此一來,好多了~ 至少很容易知道我修改了哪些數據,以及程序修改的理念與邏輯概念等等, 在維護上面是輕鬆不少不少的喔!

另外,在一些環境的設定上面,畢竟每一個人的環境都不相同,爲了取得較佳的執行環境, 我都會自行先定義好一些必定會被用到的環境變量,例如 PATH 這個玩意兒! 這樣比較好啦~因此說,建議您必定要養成良好的 script 撰寫習慣, 在每一個 script 的文件頭處記錄好:
script 的功能; 
script 的版本信息; 
script 的做者與聯絡方式; 
script 的版權宣告方式; 
script 的 History (歷史紀錄); 
script 內較特殊的指令,使用絕對路徑的方式來下達; 
script 運做時須要的環境變量預先宣告與設定。

--------------------------------------------------------------------------------
簡單的 shell script 練習

在第一支 shell script 撰寫完畢以後,相信您應該具備基本的撰寫功力了。 接下來,在開始更深刻的程序概念以前,咱們先來玩一些比較有趣的簡單的小范例好了。 底下的範例中,達成結果的方式至關的多,建議您先自行撰寫看看,寫完以後再與鳥哥寫的內容比對, 這樣才能更加深概念喔!好!不囉唆,咱們就一個一個來玩吧!


--------------------------------------------------------------------------------

變量內容由使用者決定 
不少時候咱們須要使用者輸入一些內容,好讓程序能夠順利運做。 簡單的來講,你們應該都有安裝過軟件的經驗,安裝的時候,他不是會問您『要安裝到那個目錄去?』嗎? 那個讓使用者輸入的數據的動做,就是讓使用者輸入變量內容啦。

你應該還記得在 bash 的時候,咱們有學到一個 read 指令吧?忘記的話,請自行回頭去閱讀一番。 如今,請你以 read 指令的用途,撰寫一個 script ,他可讓使用者輸入:1 first name 與 2. last name, 最後而且在屏幕上顯示:『Your full name is: 』的內容:
[root@linux scripts]# vi sh02.sh
#!/bin/bash
# Program:
#         Let user keyin their first and last name, and show their full name.
# History:
# 2005/08/23        VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

read -p "Please input your first name: " firstname
read -p "Please input your last name:  " lastname
echo -e " Your full name is: $firstname $lastname"


將上面這個 sh02.sh 執行一下,你就可以發現使用者本身輸入的變量能夠被取用的哩! 很不錯吧!加油!



--------------------------------------------------------------------------------

利用 date 進行檔案的創建 
想象一個情況,若是我天天要進行備份,而備份的數據又不想被覆蓋掉,也就是說, 我想要將天天備份的數據放在不一樣的檔案中。哇!這真困擾啊?難道要我天天去修改 script ? 不須要啊!由於天天的『日期』並不相同,因此我能夠將檔名取成相似: backup.20050802 , 不就能夠天天一個不一樣檔名了嗎?呵呵!確實如此。好了,接下來出個例子: 我想要創建三個空的檔案,檔名最開頭由使用者輸入決定,假設使用者輸入 filename 好了, 那今天的日期是 2005/08/23 ,我想要之前天、昨天、今天的日期來創建這個檔案,亦即 filename_20050821, filename_20050822, filename_20050823 ,該如何是好?
[root@linux scripts]# vi sh03.sh
#!/bin/bash
# Program:
#         User can keyin filename to touch 3 new files.
# History:
# 2005/08/23        VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

# 1. 讓使用者輸入文件名稱,並取得 fileuser 這個變量;
echo -e "I will use 'touch' command to create 3 files."
read -p "Please input the filename what you want: " fileuser

# 2. 爲了不使用者隨意按 Enter ,利用變量功能分析文件名是否有設定?
filename=${fileuser:-"filename"}

# 3. 開始利用 date 指令來取得所須要的檔名了;
date1=`date --date='2 days ago' +%Y%m%d`
date2=`date --date='1 days ago' +%Y%m%d`
date3=`date +%Y%m%d`
file1="$filename""$date1"
file2="$filename""$date2"
file3="$filename""$date3"

# 4. 將檔名創建吧!
touch $file1
touch $file2
touch $file3


我透過一些簡單的動做,這些動做均可以在 bash 那一章裏面找到, 包括小指令 (`) 的取得訊息、變量的設定功能、變量的累加以及利用 touch 指令輔助! 若是您開始執行這個 sh03.sh 以後,你能夠進行兩次輸入,一次直接按 [Enter] 來查閱檔名是啥? 一次能夠輸入一些字符,這樣來判斷你的檔案喔!關於 date 的指令應用,請 man date 吧! ^_^



--------------------------------------------------------------------------------

數值運算的方法 
各位看官應該還記得,咱們可使用 declare 來定義變量的類型吧?! 這樣纔可以進行加減運算啊!惋惜的是, bash shell 裏頭預設僅支持到整數的數據。 OK!那咱們來玩玩看,若是咱們要使用者輸入兩個變量,而後將兩個變量的內容相乘, 最後輸出相乘的結果,那能夠怎麼作?
[root@linux scripts]# vi sh04.sh
#!/bin/bash
# Program:
#         User can input 2 integer to cross by!
# History:
# 2005/08/23        VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e "You SHOULD input 2 number, I will cross they! "
read -p "first number:  " firstnu
read -p "second number: " secnu
total=$(($firstnu*$secnu))
echo -e " The number $firstnu x $secnu is ==>; $total"


在數字的運算上,咱們可使用『 declare -i total=$firstnu*$secnu 』 也可使用上面的方式來進行!基本上,鳥哥比較建議使用這樣的方式來進行運算: 
var=$((運算內容))
不但容易記憶,並且也比較方便的多~將來您可使用這種方式來計算的呀!至於數值運算上的處理, 則有:+, -, *, /, %等等。 那個 % 是取餘數啦~舉例來講, 13 對 3 取餘數,結果是 13=4*3+1,因此餘數是 1 啊!就是:
[root@linux scripts]# nu=$((13%3)); echo $nu
1


這樣瞭解了吧?!多多學習與應用喔! ^_^


--------------------------------------------------------------------------------
善用判斷式

在 bash 章節中,咱們提到過 $? 這個變量所表明的意義, 此外,也透過 && 及 || 來做爲前一個指令是否可以成功進行的一個參考。 那麼,若是我想要知道 /dmtsai 這個目錄是否存在時,難道必定要使用 ls 來執行, 而後再以 $? 來判斷執行成果嗎?呵呵!固然不須要! 咱們能夠透過『 test 』這個指令來偵測呢!


--------------------------------------------------------------------------------
利用 test 指令的測試功能

當我要檢測系統上面某些檔案或者是相關的屬性時,利用 test 這個指令來工做, 真是好用得不得了,舉例來講,我要檢查 /dmtsai 是否存在時,使用:
[root@linux ~]# test -e /dmtsai


執行結果並不會顯示任何訊息,但最後咱們能夠透過 $? 或 && 及 || 來展示整個結果呢! 例如咱們在將上面的例子改寫成這樣:
[root@linux ~]# test -e /dmtsai && echo "exist" || echo "Not exist"


最終的結果能夠告知咱們是『exist』仍是『Not exist』呢!那我知道 -e 是測試一個『東西』在不在, 若是還想要測試一下該檔名是啥玩意兒時,還有哪些標誌能夠來判斷的呢?呵呵!有底下這些東西喔!

測試的標誌 表明意義 
1. 關於某個檔名的『類型』偵測(存在與否),如 test -e filename 
-e 該『檔名』是否存在?(經常使用) 
-f 該『檔名』是否爲檔案(file)?(經常使用) 
-d 該『文件名』是否爲目錄(directory)?(經常使用) 
-b 該『檔名』是否爲一個 block device 裝置? 
-c 該『檔名』是否爲一個 character device 裝置? 
-S 該『檔名』是否爲一個 Socket 檔案? 
-p 該『檔名』是否爲一個 FIFO (pipe) 檔案? 
-L 該『檔名』是否爲一個連結檔? 
2. 關於檔案的權限偵測,如 test -r filename 
-r 偵測該檔名是否具備『可讀』的屬性? 
-w 偵測該檔名是否具備『可寫』的屬性? 
-x 偵測該檔名是否具備『可執行』的屬性? 
-u 偵測該文件名是否具備『SUID』的屬性? 
-g 偵測該文件名是否具備『SGID』的屬性? 
-k 偵測該文件名是否具備『Sticky bit』的屬性? 
-s 偵測該檔名是否爲『非空白檔案』? 
3. 兩個檔案之間的比較,如: test file1 -nt file2 
-nt (newer than)判斷 file1 是否比 file2 新 
-ot (older than)判斷 file1 是否比 file2 舊 
-ef 判斷 file2 與 file2 是否爲同一檔案,可用在判斷 hard link 的斷定上。 主要意義在斷定,兩個檔案是否均指向同一個 inode 哩! 
4. 關於兩個整數之間的斷定,例如 test n1 -eq n2 
-eq 兩數值相等 (equal) 
-ne 兩數值不等 (not equal) 
-gt n1 大於 n2 (greater than) 
-lt n1 小於 n2 (less than) 
-ge n1 大於等於 n2 (greater than or equal) 
-le n1 小於等於 n2 (less than or equal) 
5. 斷定字符串的數據 
test -z string 斷定字符串是否爲 0 ?若 string 爲空字符串,則爲 true 
test -n string 斷定字符串是否非爲 0 ?若 string 爲空字符串,則爲 false。
注: -n 亦可省略 
test str1 = str2 斷定 str1 是否等於 str2 ,若相等,則回傳 true 
test str1 != str2 斷定 str1 是否不等於 str2 ,若相等,則回傳 false 
6. 多重條件斷定,例如: test -r filename -a -x filename 
-a (and)兩情況同時成立!例如 test -r file -a -x file,則 file 同時具備 r 與 x 權限時,纔回傳 true。 
-o (or)兩情況任何一個成立!例如 test -r file -o -x file,則 file 具備 r 或 x 權限時,就可回傳 true。 
! 反相狀態,如 test ! -x file ,當 file 不具備 x 時,回傳 true 

OK!如今咱們就利用 test 來幫咱們寫幾個簡單的例子。首先,判斷一下, 讓使用者輸入一個檔名,咱們判斷:
這個檔案是否存在,若不存在則給予一個『Filename does not exist』的訊息,並中斷程序; 
若這個檔案存在,則判斷他是個檔案或目錄,結果輸出『Filename is regular file』或 『Filename is directory』 
判斷一下,執行者的身份對這個檔案或目錄所擁有的權限,並輸出權限數據!
你能夠先自行創做看看,而後再跟底下的結果討論討論。注意利用 test 與 && 還有 || 等標誌!
[root@linux scripts]# vi sh05.sh
#!/bin/bash
# Program:
#         Let user input a filename, the program will search the filename
#        1.) exist? 2.) file/directory? 3.) file permissions 
# History:
# 2005/08/25        VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

# 1. 讓使用者輸入檔名,而且判斷使用者是否真的有輸入字符串?
echo -e "The program will show you that filename is exist which input by you. "
read -p "Input a filename : " filename
test -z $filename && echo "You MUST input a filename." && exit 0
# 2. 判斷檔案是否存在?
test ! -e $filename && echo "The filename $filename DO NOT exist" && exit 0
# 3. 開始判斷檔案類型與屬性
test -f $filename && filetype="regulare file"
test -d $filename && filetype="directory"
test -r $filename && perm="readable"
test -w $filename && perm="$perm writable"
test -x $filename && perm="$perm executable"
# 4. 開始輸出信息!
echo "The filename: $filename is a $filetype"
echo "And the permission are : $perm"


頗有趣的例子吧!您能夠自行再以其它的案例來撰寫一下可用的功能呢!



--------------------------------------------------------------------------------
利用判斷符號 [ ]

除了咱們很喜歡使用的 test 以外,其實,咱們還能夠利用判斷符號『 [ ] 』來進行數據的判斷呢! 舉例來講,若是我想要知道 $HOME 這個變量是否爲空的,能夠這樣作:
[root@linux ~]# [ -z "$HOME" ]


但使用 [] 要特別注意的是,在上述的每一個組件中間都須要有空格鍵來分隔,假設我空格鍵使用『□』來表示, 那麼,在這些地方你都須要有空格鍵:
[  "$HOME"  ==  "$MAIL"  ]
[□"$HOME"□==□"$MAIL"□]
↑       ↑  ↑       ↑


上面的例子在說明,兩個字符串 $HOME 與 $MAIL 是否相同的意思,至關於 test $HOME = $MAIL 的意思啦! 而若是沒有空白分隔,例如 [$HOME==$MAIL] 時,咱們的 bash 就會顯示錯誤訊息了!這可要很注意啊! 因此說,您最好要注意:
在中括號 [] 內的每一個組件都須要有空格鍵來分隔; 
在中括號內的變量,最好都以雙引號來設定; 
在中括號內的常數,最好都以單或雙引號來設定。
舉例來講,假如我設定了 name="VBird Tsai" ,而後這樣斷定:
[root@linux ~]# name="VBird Tsai"
[root@linux ~]# [ $name == "VBird" ]
bash: [: too many arguments


爲何呢?由於 $name 若是沒有使用雙引號刮起來,那麼上面的斷定式會變成: 
[ VBird Tsai == "VBird" ] 
而不是咱們要的: 
[ "VBird Tsai" == "VBird" ] 
這但是差不少的喔!另外,中括號的使用方法與標誌與 test 幾乎如出一轍啊~ 只是中括號比較經常使用在條件判斷式 if ..... then ..... fi 的狀況中就是了。 好,那咱們也繼續來作一個小案例好了:
當執行一個程序的時候,這個程序會讓使用者選擇 Y 或 N , 
若是使用者輸入 Y 或 y 時,就顯示『 OK, continue 』 
若是使用者輸入 n 或 N 時,就顯示『 Oh, interrupt !』 
若是不是 Y/y/N/n 以內的其它字符,就顯示『I don't know what is your choise』
利用中括號、 && 與 || 來繼續吧!
[root@linux scripts]# vi sh06.sh
#!/bin/bash
# Program:
#         This program will show the user's choice
# History:
# 2005/08/25        VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

read -p "Please input (Y/N): " yn
[ "$yn" == "Y" -o "$yn" == "y" ] && echo "OK, continue" && exit 0
[ "$yn" == "N" -o "$yn" == "n" ] && echo "Oh, interrupt!" && exit 0
echo "I don't know what is your choise" && exit 0


頗有趣吧!利用這個字符串判別的方法,咱們就能夠很輕鬆的將使用者想要進行的工做分門別類呢! 接下來,咱們再來談一些其它有的沒有的東西吧!

Tips:
爲何判斷式裏面下達等於要用 == 而不是一個 = 就行了呢?咱們在前一章正規表示法裏面的 awk 提到, 只有一個 = 用來給予一個變量設定其內容,邏輯判斷時,則會給予兩個等於, 亦即『比較』而非『設定』的意思~這裏要好好的分辨一下喔! ^_^   


--------------------------------------------------------------------------------
Shell script 的預設變數($0, $1...)

其實,當咱們執行一個 shell script 時,在這個 shell script 裏面就已將幫咱們作好一些可用的變量了。 舉例來講,在不久的未來,您就會發現,當咱們要啓動一個系統服務時,可能會下達相似這樣的指令:
[root@linux ~]# /etc/init.d/crond restart


那是啥玩意兒?呵呵!就是『向 /etc/init.d/crond 這個 script 下達 restart 的指令』, 咦!咱們不是都使用 read 來讀取使用者輸入的變量內容嗎?爲啥我能夠直接在 script 後面接上這個參數? 這是由於 shell script 幫咱們設定好一些指定的變量了!變量的對應是這樣的:

/path/to/scriptname  opt1  opt2  opt3  opt4  ...
       $0             $1    $2    $3    $4   ...


這樣夠清楚了吧?!執行的文件名爲 $0 這個變量,第一個接的參數就是 $1 啊~ 因此,只要咱們在 script 裏面善用 $1 的話,就能夠很簡單的當即下達某些指令功能了! 好了,來作個例子吧~假設我要執行一個 script ,執行後,該 script 會自動列出本身的檔名, 還有後面接的前三個參數,該如何是好?
[root@linux scripts]# vi sh07.sh
#!/bin/bash
# Program:
#         The program will show it's name and first 3 parameters.
# History:
# 2005/08/25        VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

echo "The script naem is ==>; $0"
[ -n "$1" ] && echo "The 1st paramter is ==>; $1" || exit 0
[ -n "$2" ] && echo "The 2nd paramter is ==>; $2" || exit 0
[ -n "$3" ] && echo "The 3th paramter is ==>; $3" || exit 0


這支程序裏面鳥哥加上了一些控制式,亦即利用 && 及 || 來加以判斷 $1 ~ $3 是否存在? 若存在才顯示,若不存在就中斷~執行結果以下:
[root@linux scripts]# sh sh07.sh theone haha quot
The script naem is ==>; sh07.sh
The 1st paramter is ==>; theone
The 2nd paramter is ==>; haha
The 3th paramter is ==>; quot


上面這七的例子都很簡單吧?幾乎都是利用 bash 的相關功能而已~ 不難啦~底下咱們就要使用條件判斷式來進行一些分別功能的設定了,好好瞧一瞧先~ 

--------------------------------------------------------------------------------
條件判斷式:

只要講到『程序』的話,那麼條件判斷式,亦便是『 if then 』這種判別式確定必定要學習的! 由於不少時候,咱們都必需要依據某些數據來判斷程序該如何進行。舉例來講,咱們在上頭不是有練習當使用者輸入 Y/N 時,必需要執行不一樣的訊息輸出嗎?簡單的方式能夠利用 && 與 || ,但若是我還想要執行一堆指令呢? 那真的得要 if then 來幫忙囉~底下咱們就來聊一聊!


--------------------------------------------------------------------------------
利用 if .... then

這個 if .... then 是最多見的條件判斷式了~簡單的說,就是當符合某個條件判斷的時候, 就予以進行某項工做就是了。咱們能夠簡單的這樣看:
if [ 條件判斷式 ]; then
        當條件判斷式成立時,能夠進行的指令工做內容;
fi


至於條件判斷式的判斷方法,與前一小節的介紹相同啊!較特別的是,若是我有多個條件要判別時, 除了 sh06.sh 那個案例,也就是將多個條件寫入一箇中括號內的狀況以外, 我還能夠有多箇中括號來隔開喔!而括號與括號之間,則以 && 或 || 來隔開,他們的意義是: 
&& 表明 AND ; 
|| 表明 or ;
因此,在使用中括號的判斷式中, && 及 || 就與指令下達的狀態不一樣了。舉例來講, sh06.sh 那個例子我能夠改寫成這樣:
[root@linux scripts]# vi sh06-2.sh
#!/bin/bash
# Program:
#         This program will show the user's choice
# History:
# 2005/08/25        VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

read -p "Please input (Y/N): " yn

if [ "$yn" == "Y" ] || [ "$yn" == "y" ]; then
        echo "OK, continue"
        exit 0
fi
if [ "$yn" == "N" ] || [ "$yn" == "n" ]; then
        echo "Oh, interrupt!"
        exit 0
fi
echo "I don't know what is your choise" && exit 0


不過,由這個例子看起來,彷佛也沒有什麼了不得吧? sh06.sh 還比較簡單呢~ 可是,若是咱們考慮底下的狀態,您就會知道 if then 的好處了:
if [ 條件判斷式 ]; then
        當條件判斷式成立時,能夠進行的指令工做內容;
else
        當條件判斷式不成立時,能夠進行的指令工做內容;
fi


若是考慮更復雜的狀況,則可使用這個語法:
if [ 條件判斷式一 ]; then
        當條件判斷式一成立時,能夠進行的指令工做內容;
elif [ 條件判斷式二 ]; then
        當條件判斷式二成立時,能夠進行的指令工做內容;
else
        當條件判斷式一與二均不成立時,能夠進行的指令工做內容;
fi


那我就能夠將 sh06-2.sh 改寫成這樣:
[root@linux scripts]# vi sh06-3.sh
#!/bin/bash
# Program:
#         This program will show the user's choice
# History:
# 2005/08/25        VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

read -p "Please input (Y/N): " yn

if [ "$yn" == "Y" ] || [ "$yn" == "y" ]; then
        echo "OK, continue"
elif [ "$yn" == "N" ] || [ "$yn" == "n" ]; then
        echo "Oh, interrupt!"
else
        echo "I don't know what is your choise"
fi


是否程序變得很簡單,並且依序判斷,能夠避免掉重複判斷的情況,這樣真的很容易設計程序的啦! ^_^ 好了,那麼若是我要偵測你所輸入的參數是否爲 hello 呢 , 也就是說,若是我想要知道,你在程序後面所接的第一個參數 (就是 $1 啊!) 是否爲 hello ,
若是是的話,就顯示 "Hello, how are you ?"; 
若是沒有加任何參數,就提示使用者必需要使用的參數下達法; 
而若是加入的參數不是 hello ,就提醒使用者僅能使用 hello 爲參數。
整個程序的撰寫能夠是這樣的:
[root@linux scripts]# vi sh08.sh
#!/bin/bash
# Program:
#         Show "Hello" from $1....
# History:
# 2005/08/28        VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

if [ "$1" == "hello" ]; then
        echo "Hello, how are you ?"
elif [ "$1" == "" ]; then
        echo "You MUST input parameters, ex>; $0 someword"
else
        echo "The only parameter is 'hello'"
fi


而後您能夠執行這支程序,分別在 $1 的位置輸入 hello, 沒有輸入與隨意輸入, 就能夠看到不一樣的輸出囉~是否還以爲挺簡單的啊! ^_^。事實上, 學到這裏,也真的很厲害了~好了,底下咱們繼續來玩一些比較大一點的囉~ 咱們在前一章已經學會了 grep 這個好用的玩意兒,那麼多學一個叫作 netstat 的指令, 這個指令能夠查詢到目前主機有開啓的網絡服務端口口 (service ports), 相關的功能咱們會在服務器架設篇繼續介紹,這裏您只要知道,我能夠利用『 netstat -tuln 』來取得目前主機有啓動的服務, 並且取得的信息有點像這樣:
[root@linux ~]# netstat -tuln
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address   Foreign Address    State
tcp        0      0 0.0.0.0:199     0.0.0.0:*          LISTEN
tcp        0      0 :::80           :::*               LISTEN
tcp        0      0 :::22           :::*               LISTEN
tcp        0      0 :::25           :::*               LISTEN


上面的重點是特殊字體的那個部分,那些特殊字體的部分表明的就是 port 囉~ 那麼每一個 port 表明的意義爲什麼呢?幾個常見的 port 與相關網絡服務的關係是: 
80: WWW 
22: ssh 
21: ftp 
25: mail
那我如何透過 netstat 去偵測個人主機是否有開啓這四個主要的網絡服務端口口呢? 我能夠簡單的這樣去寫這個程序喔:
[root@linux scripts]# vi sh09.sh
#!/bin/bash
# Program:
#         Using netstat and grep to detect WWW,SSH,FTP and Mail services.
# History:
# 2005/08/28        VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

# 1. 先做一些告知的動做而已~
echo "Now, the services of your Linux system will be detect!"
echo -e "The www, ftp, ssh, and mail will be detect! "

# 2. 開始進行一些測試的工做,而且也輸出一些信息囉!
testing=`netstat -tuln | grep ":80 "`
if [ "$testing" != "" ]; then
        echo "WWW is running in your system."
fi
testing=`netstat -tuln | grep ":22 "`
if [ "$testing" != "" ]; then
        echo "SSH is running in your system."
fi
testing=`netstat -tuln | grep ":21 "`
if [ "$testing" != "" ]; then
        echo "FTP is running in your system."
fi
testing=`netstat -tuln | grep ":25 "`
if [ "$testing" != "" ]; then
        echo "Mail is running in your system."
fi


這樣又可以一個一個的檢查囉~是否頗有趣啊! ^_^。接下來,咱們再來玩更難一點的。 咱們知道能夠利用 date 來顯示日期與時間,也能夠利用 $((計算式)) 來計算數值運算。 另外, date 也能夠用來顯示自 19710101 以來的『總秒數』 (請自行查閱 man date 及 info date) 。那麼,您是否能夠撰寫一支小程序,用來『計算退伍日期還剩幾天?』也就是說:
先讓使用者輸入他們的退伍日期; 
再由如今日期比對退伍日期; 
由兩個日期的比較來顯示『還須要幾天』纔可以退伍的字樣。
彷佛挺難的樣子?其實也不會啦,利用『 date --date="YYYYMMDD" +%s 』就可以達到咱們所想要的囉~若是您已經寫完了程序,對照底下的寫法試看看:
[root@linux scripts]# vi sh10.sh
#!/bin/bash
# Program:
#         Tring to calculate your demobilization date at how many days 
#        later...
# History:
# 2005/08/29        VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

# 1. 告知使用者這支程序的用途,而且告知應該如何輸入日期格式?
echo "This program will try to calculate :"
echo "How many days about your demobilization date..."
read -p "Please input your demobilization date (YYYYMMDD ex>;20050401): " date2

# 2. 測試一下,這個輸入的內容是否正確?利用正規表示法囉~
date_d=`echo $date2 |grep '[0-9]{8}'`
if [ "$date_d" == "" ]; then
        echo "You input the wrong format of date...."
        exit 1
fi

# 3. 開始計算日期囉~
declare -i date_dem=`date --date="$date2" +%s`
declare -i date_now=`date +%s`
declare -i date_total_s=$(($date_dem-$date_now))
declare -i date_d=$(($date_total_s/60/60/24))
if [ "$date_total_s" -lt "0" ]; then
        echo "You had been demobilization before: " $((-1*$date_d)) " ago"
else
        declare -i date_h=$(($(($date_total_s-$date_d*60*60*24))/60/60))
        echo "You will be demobilized after $date_d days and $date_h hours."
fi


瞧一瞧,這支程序能夠幫您計算退伍日期呢~若是是已經退伍的朋友, 還能夠知道已經退伍多久了~哈哈!很可愛吧~利用 date 算出自 1971/01/01 以來的總秒數, 再與目前的總秒數來比對,而後以一天的總秒數 (60*60*24) 爲基數去計算總日數, 就可以得知二者的差別了~瞧~所有的動做都沒有超出咱們所學的範圍吧~ ^_^ 還可以避免使用者輸入錯誤的數字,因此多了一個正規表示法的判斷式呢~ 這個例子比較難,有興趣想要一探究竟的朋友,能夠做一下課後練習題 關於計算生日的那一題喔!~加油!



--------------------------------------------------------------------------------
利用 case ..... esac 判斷

上個小節提到的『 if .... then .... fi 』對於變量的判斷中, 是以比對的方式來分辨的,若是符合狀態就進行某些行爲,而且透過較多層次 ( 就是 elif ... ) 的方式來進行多個變量的程序代碼撰寫,譬如 sh08.sh 那個小程序,就是用這樣的方式來的囉。 好,那麼萬一我有多個既定的變量內容,例如 sh08.sh 當中,我所須要的變量就是 "hello" 及空字符串兩個, 那麼我只要針對這兩個變量來設定情況就行了對吧?!那麼可使用什麼方式來設計呢? 呵呵~就用 case ... in .... esac 吧~,他的語法以下:
case $變量名稱 in
  "第一個變量內容")
        程序段
        ;;
  "第二個變量內容")
        程序段
        ;;
  *)
        不包含第一個變量內容與第二個變量內容的其它程序執行段
        exit 1
        ;;
esac


要注意的是,這個語法是以 case 爲開頭,而以 esac 爲結尾,啥?爲什麼是 esac 呢?想想,既然 if 的結尾是 fi ,那麼 case 的結尾固然就是將 case 倒着寫,天然就是 esac 囉~ ^_^,很好記吧~ 另外,每個變量內容的程序段最後都須要兩個分號 (;;) 來表明該程序段落的結束,這挺重要的喔! 至於爲什麼須要有 * 這個變量內容在最後呢?這是由於,若是使用者不是輸入變量內容一或二時, 咱們能夠告知使用者相關的信息啊!舉例來講,咱們若是將 sh08.sh 改寫的話, 他應該會變成這樣喔!
[root@linux scripts]# vi sh08-2.sh
#!/bin/bash
# Program:
#         Show "Hello" from $1.... by using case .... esac
# History:
# 2005/08/29        VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

case $1 in
  "hello")
        echo "Hello, how are you ?"
        ;;
  "")
        echo "You MUST input parameters, ex>; $0 someword"
        ;;
  *)
        echo "Usage $0 {hello}"
        ;;
esac


在上面這個 sh08-2.sh 的案例當中,若是你輸入『 sh sh08-2.sh test 』來執行, 那麼屏幕上就會出現『Usage sh08-2.sh {hello}』的字樣,告知執行者僅可以使用 hello 喔~ 這樣的方式對於須要某些固定字符串來執行的變量內容就顯的更加的方便呢? 這種方式您真的要熟悉喔!這是由於系統的不少服務的啓動 scripts 都是使用這種寫法的, 舉例來講,咱們 Linux 的服務啓動放置目錄是在 /etc/init.d/ 當中,我已經知道里頭有個 syslog 的服務,我想要從新啓動這個服務,能夠這樣作: 
/etc/init.d/syslog restart
重點是那個 restart 啦~若是您進入 /etc/init.d/syslog 就會看到他使用的是 case 語法, 而且會規定某些既定的變量內容,你能夠直接下達 /etc/init.d/syslog , 該 script 就會告知你有哪些後續接的變量可使用囉~方便吧! ^_^

通常來講,使用『 case $變量 in 』這個語法中,當中的那個 $變量 大體有兩種取得的方式:
直接下達式:例如上面提到的,利用『 script.sh variable 』 的方式來直接給予 $1 這個變量的內容,這也是在 /etc/init.d 目錄下大多數程序的設計方式。 
交互式:透過 read 這個指令來讓使用者輸入變量的內容。
這麼說或許您的感覺性還不高,好,咱們直接寫個程序來玩玩:讓使用者可以輸入 one, two, three , 而且將使用者的變量顯示到屏幕上,若是不是 one, two, three 時,就告知使用者僅有這三種選擇。
[root@linux scripts]# vi sh11.sh
#!/bin/bash
# Program:
#         Let user input one, two, three and show in screen.
# History:
# 2005/08/29        VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

echo "This program will print your selection !"
# read -p "Input your choice: " choice
# case $choice in
case $1 in
  "one")
        echo "Your choice is ONE"
        ;;
  "two")
        echo "Your choice is TWO"
        ;;
  "three")
        echo "Your choice is THREE"
        ;;
  *)
        echo "Usage {one|two|three}"
        ;;
esac


此時,您可使用『 sh sh11.sh two 』的方式來下達指令,就能夠收到相對應的響應了。 上面使用的是直接下達的方式,而若是使用的是交互式時,那麼將上面第 10, 11 行的 "#" 拿掉, 並將 12 行加上批註 (#),就可讓使用者輸入參數囉~這樣是否頗有趣啊?!



--------------------------------------------------------------------------------
利用 function 功能

什麼是『函數 (function)』功能啊?簡單的說,其實, 函數能夠在 shell script 當中作出一個相似自訂執行指令的東西,最大的功能是, 能夠簡化咱們不少的程序代碼~舉例來講,上面的 sh11.sh 當中,每一個輸入結果 one, two, three 其實輸出的內容都同樣啊~那麼我就可使用 function 來簡化了! function 的語法是這樣的:
function fname() {
        程序段
}


那個 fname 就是咱們的自訂的執行指令名稱~而程序段就是咱們要他執行的內容了。 要注意的是,在 shell script 當中, function 的設定必定要在程序的最前面, 這樣纔可以在執行時被找到可用的程序段喔!好~咱們將 sh11.sh 改寫一下:
[root@linux scripts]# vi sh11-2.sh
#!/bin/bash
# Program:
#         Let user input one, two, three and show in screen.
# History:
# 2005/08/29        VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

function printit(){
        echo -n "Your choice is "
}

echo "This program will print your selection !"
case $1 in
  "one")
        printit; echo $1 | tr 'a-z' 'A-Z'
        ;;
  "two")
        printit; echo $1 | tr 'a-z' 'A-Z'
        ;;
  "three")
        printit; echo $1 | tr 'a-z' 'A-Z'
        ;;
  *)
        echo "Usage {one|two|three}"
        ;;
esac


以上面的例子來講,我作了一個函數名稱爲 printif ,因此,當我在後續的程序段裏面, 只要執行 printit 的話,就表示個人 shell script 要去執行『 function printit .... 』 裏面的那幾個程序段落囉! 固然囉,上面這個例子舉得太簡單了,因此您不會以爲 function 有什麼好厲害的, 不過,若是某些程序代碼一再地在 script 當中重複時,這個 function 可就重要的多囉~ 不但能夠簡化程序代碼,並且能夠作成相似『模塊』的玩意兒,真的很棒啦!

另外, function 也是擁有內建變量的~他的內建變量與 shell script 很相似, 函數名稱表明示 $0 ,然後續接的變量也是以 $1, $2... 來取代的~ 這裏很容易搞錯喔~由於『 function fname() { 程序段 } 』內的 $0, $1... 等等與 shell script 的 $0 是不一樣的。以上面 sh11-2.sh 來講,假如我下達:『 sh sh11-2.sh one 』 這表示在 shell script 內的 $1 爲 "one" 這個字符串。可是在 printit() 內的 $1 則與這個 one 無關。 咱們將上面的例子再次的改寫一下,讓您更清楚!
[root@linux scripts]# vi sh11-3.sh
#!/bin/bash
# Program:
#         Let user input one, two, three and show in screen.
# History:
# 2005/08/29        VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

function printit(){
        echo "Your choice is $1"
}

echo "This program will print your selection !"
case $1 in
  "one")
        printit 1
        ;;
  "two")
        printit 2
        ;;
  "three")
        printit 3
        ;;
  *)
        echo "Usage {one|two|three}"
        ;;
esac


在上面的例子當中,若是您輸入『 sh sh11-3.sh one 』就會出現『 Your choice is 1 』的字樣~ 爲何是 1 呢?由於在程序段落當中,咱們是寫了『 printit 1 』那個 1 就會成爲 function 當中的 $1 喔~ 這樣是否理解呢? function 自己其實比較困難一點,若是您還想要進行其它的撰寫的話。 不過,咱們僅是想要更加了解 shell script 而已,因此,這裏看看便可~瞭解原理就好囉~ ^_^ 

--------------------------------------------------------------------------------
循環 (loop)

除了 if...then...fi 這種條件判斷式以外,循環多是程序當中最重要的一環了~ 循環能夠不斷的執行某個程序段落,直到使用者設定的條件達成爲止。 因此,重點是那個『條件的達成』是什麼。底下咱們就來談一談:


--------------------------------------------------------------------------------
while do done, until do done

通常來講,循環最多見的就是底下這兩種狀態了:
while [ condition ]
do
        程序段落
done


這種方式中, while 是『當....時』,因此,這種方式說的是『當 condition 條件成立時,就進行循環,直到 condition 的條件不成立才中止』的意思。
until [ condition ]
do
        程序段落
done


這種方式偏偏與 while 相反,它說的是『當 condition 條件成立時,就終止循環, 不然就持續進行循環的程序段。』是否恰好相反啊~咱們以 while 來作個簡單的練習好了。 假設我要讓使用者輸入 yes 或者是 YES 才結束程序的執行,不然就一直進行告知使用者輸入字符串。
[root@linux scripts]# vi sh12.sh
#!/bin/bash
# Program:
#         Use loop to try find your input.
# History:
# 2005/08/29        VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

while [ "$yn" != "yes" ] && [ "$yn" != "YES" ]
do
        read -p "Please input yes/YES to stop this program: " yn
done


上面這個例題的說明是『當 $yn 這個變量不是 "yes" 且 $yn 也不是 "YES" 時,才進行循環內的程序。』 而若是 $yn 是 "yes" 或 "YES" 時,就會離開循環囉~那若是使用 until 呢?呵呵有趣囉~ 他的條件會變成這樣:
[root@linux scripts]# vi sh12-2.sh
#!/bin/bash
# Program:
#         Use loop to try find your input.
# History:
# 2005/08/29        VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

until [ "$yn" == "yes" ] || [ "$yn" == "YES" ]
do
        read -p "Please input yes/YES to stop this program: " yn
done


仔細比對一下這兩個東西有啥不一樣喔! ^_^再來,若是我想要計算 1+2+3+....+100 這個數據呢? 利用循環啊~他是這樣的:
[root@linux scripts]# vi sh13.sh
#!/bin/bash
# Program:
#         Try to use loop to calculate the result "1+2+3...+100"
# History:
# 2005/08/29        VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

s=0
i=0
while [ "$i" != "100" ]
do
        i=$(($i+1))
        s=$(($s+$i))
done
echo "The result of '1+2+3+...+100' is ==>; $s"


嘿嘿!當您執行了『 sh sh13.sh 』以後,就能夠獲得 5050 這個數據纔對啊!這樣瞭呼~ 那麼讓您自行作一下,若是想要讓使用者自行輸入一個數字,讓程序由 1+2+... 直到您輸入的數字爲止, 該如何撰寫呢?應該很簡單吧?!答案能夠參考一下習題練習裏面的一題喔!



--------------------------------------------------------------------------------
for...do....done

相對於 while, until 的循環方式是必需要『符合某個條件』的狀態, for 這種語法,則是『 已經知道要進行幾回循環』的狀態!他的語法是:
for (( 初始值; 限制值; 執行步階 ))
do
        程序段
done


這種語法適合於數值方式的運算當中,在 for 後面的括號內的三串內容意義爲:
初始值:某個變量在循環當中的起始值,直接以相似 i=1 設定好; 
限制值:當變量的值在這個限制值的範圍內,就繼續進行循環。例如 i<=100; 
執行步階:每做一次循環時,變量的變化量。例如 i=i+1。
值得注意的是,在『執行步階』的設定上,若是每次增長 1 ,則可使用相似『i++』的方式,亦便是 i 每次循環都會增長一的意思。好,咱們以這種方式來進行 1 累加到 100 的循環吧!
[root@linux scripts]# vi sh14.sh
#!/bin/bash
# Program:
#         Try do calculate 1+2+....+100
# History:
# 2005/08/29        VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

s=0
for (( i=1; i<=100; i=i+1 ))
do
        s=$(($s+$i))
done
echo "The result of '1+2+3+...+100' is ==>; $s"


同樣也是很簡單吧!利用這個 for 則能夠直接限制循環要進行幾回呢!這麼好用的東西難道只能在數值方面動做? 固然不是啦~咱們還能夠利用底下的方式來進行非數字方面的循環運做喔!
for $var in con1 con2 con3 ...
do
        程序段
done


以上面的例子來講,這個 $var 的變量內容在循環工做時:
第一次循環時, $var 的內容爲 con1 ; 
第二次循環時, $var 的內容爲 con2 ; 
第三次循環時, $var 的內容爲 con3 ; 
....
咱們能夠作個簡單的練習。假設我有三種動物,分別是 dog, cat, elephant 三種, 我想每一行都輸出這樣:『There are dogs...』之類的字樣,則能夠:
[root@linux scripts]# vi sh15.sh
#!/bin/bash
# Program:
#         Using for .... loop to print 3 animal 
# History:
# 2005/08/29        VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

for animal in dog cat elephant
do
        echo "There are ""$animal""s.... "
done


很簡單是吧! ^_^。好了,那麼若是我想要讓使用者輸入某個目錄, 而後我找出某目錄內的文件名的權限呢?又該如何是好?呵呵!能夠這樣作啦~
[root@linux scripts]# vi sh16.sh
#!/bin/bash
# Program:
#         let user input a directory and find the whole file's permission.
# History:
# 2005/08/29        VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

# 1. 先看看這個目錄是否存在啊?
read -p "Please input a directory: " dir
if [ "$dir" == "" ] || [ ! -d "$dir" ]; then
        echo "The $dir is NOT exist in your system."
        exit 1
fi

# 2. 開始測試檔案囉~
filelist=`ls $dir`
for filename in $filelist
do
        perm=""
        test -r "$dir/$filename" && perm="$perm readable"
        test -w "$dir/$filename" && perm="$perm writable"
        test -x "$dir/$filename" && perm="$perm executable"
        echo "The file $dir/$filename's permission is $perm "
done


呵呵!頗有趣的例子吧~利用這種方式,您能夠很輕易的來處理一些檔案的特性呢~ 咱們循環就介紹到這裏了~其它更多的應用,就得視您的需求來玩囉~。 

--------------------------------------------------------------------------------
shell script 的追蹤與 debug

scripts 在執行以前,最怕的就是出現問題了!那麼咱們如何 debug 呢?有沒有辦法不須要透過直接執行該 scripts 就能夠來判斷是否有問題呢!?呵呵! 固然是有的!咱們就直接以 bash 的相關參數來進行判斷吧!
[root@linux ~]# sh [-nvx] scripts.sh
參數:
-n  :不要執行 script,僅查詢語法的問題;
-v  :再執行 sccript 前,先將 scripts 的內容輸出到屏幕上;
-x  :將使用到的 script 內容顯示到屏幕上,這是頗有用的參數!
範例:

範例一:測試 sh16.sh 有無語法的問題?
[root@linux ~]# sh -n sh16.sh 
# 若語法沒有問題,則不會顯示任何信息!

範例二:將 sh15.sh 的執行過程所有列出來~
[root@linux ~]# sh -x sh15.sh 
+ PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/home/vbird/bin
+ export PATH
+ for animal in dog cat elephant
+ echo 'There are dogs.... '
There are dogs....
+ for animal in dog cat elephant
+ echo 'There are cats.... '
There are cats....
+ for animal in dog cat elephant
+ echo 'There are elephants.... '
There are elephants....
# 使用 -x 真的是追蹤 script 的好方法,他能夠將全部有執行的程序段在執行前列出來,
# 若是是程序段落,則輸出時,最前面會加上 + 字號,表示他是程序代碼而已,
# 實際的輸出則與 standard output 有關啊~如上所示。


在上面的範例二當中,咱們能夠透過這個簡單的參數 -x 來達成 debug 的目的,這但是一個不可多得的參數, 一般若是您執行 script 卻發生問題時,利用這個 -x 參數,就能夠知道問題是發生在哪一行上面了!

熟悉 sh 的用法,將可使您在管理 Linux 的過程當中駕輕就熟!至於在 Shell scripts 的學習方法上面,須要『多看、多模仿、並加以修改爲本身的樣式!』 是最快的學習手段了!網絡上有至關多的朋友在開發一些至關有用的 scripts ,如果您能夠將對方的 scripts 拿來,而且改爲適合本身主機的樣子!那麼學習的效果會是最快的呢!

另外,咱們 Linux 系統原本就有不少的啓動 script ,若是您想要知道每一個 script 所表明的功能是什麼? 能夠直接以 vi 進入該 script 去查閱一下,一般馬上就知道該 script 的目的了。 舉例來講,咱們的 Linux 裏頭有個文件名稱爲: /etc/init.d/portmap ,這個 script 是幹嗎用的? 利用 vi 去查閱最前面的幾行字,他出現以下信息:
# description: The portmapper manages RPC connections, which are used by 
#              protocols such as NFS and NIS. The portmap server must be 
#              running on machines which act as servers for protocols which 
#              make use of the RPC mechanism.
# processname: portmap


簡單的說,他是被用在 NFS 與 NIS 上面的一個啓動 RPC 的 script , 而後咱們再利用 http://www.google.com.tw 去搜尋一下 NFS, NIS 與 RPC , 馬上就可以知道這個 script 的功能囉~因此,下次您發現不明的 script 時, 若是是系統提供的,那麼利用這個檢查的方式,必定能夠約略瞭解的啦! 加油的囉~ ^_^

另外,本章全部的範例均可以在 http://linux.vbird.org/linux_basic/0340bashshell-scripts/scripts.tgz 裏頭找到喔!加油~



--------------------------------------------------------------------------------
本章習題練習
( 要看答案請將鼠標移動到『答:』底下的空白處,按下左鍵圈選空白處便可察看 ) 
請創建一支 script ,當你執行該 script 的時候,該 script 能夠顯示: 1. 你目前的身份 (用 whoami ) 2. 你目前所在的目錄 (用 pwd) 
#!/bin/bash
echo -e "Your name is ==>; `whoami`"
echo -e "The current directory is ==>; `pwd`"

請自行創建一支程序,該程序能夠用來計算『您還有幾天能夠過生日』啊?? 
#!/bin/bash
read -p "Pleas input your birthday (MMDD, ex>; 0709): " bir
now=`date +%m%d`
if [ "$bir" == "$now" ]; then
echo "Happy Birthday to you!!!"
elif [ "$bir" -gt "$now" ]; then
year=`date +%Y`
total_d=$(($((`date --date="$year$bir" +%s`-`date +%s`))/60/60/24))
echo "Your birthday will be $total_d later"
else
year=$((`date +%Y`+1))
total_d=$(($((`date --date="$year$bir" +%s`-`date +%s`))/60/60/24))
echo "Your birthday will be $total_d later"
fi

讓使用者輸入一個數字,程序能夠由 1+2+3... 一直累加到使用者輸入的數字爲止。 
#!/bin/bash
read -p "Please input an integer number: " number
i=0
s=0
while [ "$i" != "$number" ]
do
i=$(($i+1))
s=$(($s+$i))
done
echo "the result of '1+2+3+...$number' is ==>; $s"

撰寫一支程序,他的做用是: 1.) 先查看一下 /root/test/logical 這個名稱是否存在; 2.) 若不存在,則創建一個檔案,使用 touch 來創建,創建完成後離開; 3.) 若是存在的話,判斷該名稱是否爲檔案,若爲檔案則將之刪除後創建一個檔案,檔名爲 logical ,以後離開; 4.) 若是存在的話,並且該名稱爲目錄,則移除此目錄! 
#!/bin/bash
if [ ! -e logical ]; then
touch logical
echo "Just make a file logical"
exit 1
elif [ -e logical ] && [ -f logical ]; then
rm logical
mkdir logical
echo "remove file ==>; logical"
echo "and make directory logical"
exit 1
elif [ -e logical ] && [ -d logical ]; then
rm -rf logical
echo "remove directory ==>; logical"
exit 1
else
echo "Does here have anything?"
fi

咱們知道 /etc/passwd 裏面以 : 來分隔,第一欄爲帳號名稱。請寫一隻程序,能夠將 /etc/passwd 的第一欄取出,並且每一欄都以一行字符串『The 1 account is "root" 』來顯示,那個 1 表示行數。 
#!/bin/bash
accounts=`cat /etc/passwd | cut -d':' -f1`
for account in $accounts
do
declare -i i=$i+1
echo "The $i account is "$account" "
donelinux

相關文章
相關標籤/搜索