shell 生活0806012145

引用:     ?
  學習 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 /n"
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 "/nYour 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! /n"
read -p "first number:? " firstnu
read -p "second number: " secnu
total=$(($firstnu*$secnu))
echo -e "/nThe 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./n/n"
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! /n"

# 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/" "
done

http://www.chinaunix.net/jh/24/628472.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/535591.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/535591.shtml
英文地址,請注明出處:http://en.pswp.cn/news/535591.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

linux系統啟動流程詳解

一、MBR的概念 主引導扇區位于硬盤的0磁道0柱面1扇區,共512bytes,可存放一小段程序及主分區表,由三大部分組成: 硬盤主引導記錄MBR(Master Boot Record)占446bytes 分區表DPT(Disk Partition Table)占64bytes 硬盤有效標志(Magic Number)占2bytes。 硬盤…

linux下安裝davinci

官網地址 https://edp963.github.io/davinci/ 1、準備 JDK 1.8&#xff08;或更高版本&#xff09; MySql5.5&#xff08;或更高版本&#xff09; Mail Server (本人測試可以不用安裝) phantomjs 或 chrome&#xff08;需同時安裝chromedriver&#xff0c;清注意版本&#xf…

GRUB詳解

一、GRUB介紹 GRUB (GRand Unified Bootloader)是GNU 下的FSF 組織所推行的一套多重開機管理軟件,目前 GRUB 在 Linux 上使用的版本為 0.97 版,FSF 的官方網站表示已經不會在此版本上再追加任何新的功能,會將心思放在 GRUB 2 上。 GNU GRUB(GRand Unified Bootloader)是一…

好用的yum

從Windows轉到Linux下面&#xff0c;一個不習慣的地方就是在圖形界面下安裝和刪除軟件的時候非常緩慢。但是如果你掌握了用yum的命令行模式進行配置程序&#xff0c;你肯定會從心底喜歡上這個強大的工具。因為yum提供了查找、安裝、刪除某一個、一組甚至全部軟件包的命令&#…

Ambari系統架構

一、Ambari系統架構 Ambari框架采用的是Server/Client的模式&#xff0c;主要由兩部分組成&#xff1a;ambari-agent和ambari-server。ambari依賴其它已經成熟的工具&#xff0c;例如其ambari-server 就依賴python&#xff0c;而ambari-agent還同時依賴ruby, puppet&#xff0…

Linux系統的基本安裝

一、制作Linux安裝啟動盤 1.準備一個干凈的U盤&#xff0c;格式化。 2.下載64位系統鏡像&#xff1a;rhel-server-6.8-x86_64-dvd.iso。64位系統直接安裝就好&#xff0c; 3.首先制作一個啟動盤&#xff0c;解壓rhel-server-6.8-i386-dvd.iso文件 4.下載安裝UltraISO&#…

linux tips 技巧筆記一

作者: Jeffrey出處: http://blog.zhangjianfeng.com/?p171 實現RedHat非正常關機的自動磁盤修復先登錄到服務器&#xff0c;然后在/etc/sysconfig里增加一個文件autofsck,內容如下&#xff1a;AUTOFSCK_DEF_CHECKyesPROMPTyes 改變文件或目錄之最后修改時間(變為當前時間)執…

Shell基本概念

一、什么是shell shell是外殼的意思&#xff0c;就是操作系統的外殼。我們可以通過shell命令來操作和控制操作系統&#xff0c;比如Linux中的Shell命令就包括ls、cd、pwd等等。總結來說&#xff0c;Shell是一個命令解釋器&#xff0c;它通過接受用戶輸入的Shell命令來啟動、暫…

Shell解釋器

shell解釋器&#xff0c;用戶和操作系統內核之間的橋梁 一、Shell常見種類 就像不同地區有不同方言一樣&#xff0c;不同的Linux/Unix系統使用著不同類型的shell Bsh:由貝爾實驗室編寫。Bsh是產生較早的UNIX Shell程序&#xff0c;實現了最基本的命令解釋器的功能&#xff0c…

mysql安裝、導入數據腳本

mysql安裝&#xff1a; #!/bin/bash if [ $(id -u) ! "0" ]; thenecho "Error: You must be root to run this script, please use root to install mysql"exit 1 fi unzip Mysql.zip cd /home/install_LFS/Mysql cur_dir$(pwd) mkdir -p /usr/local/mysq…

linux tips 技巧筆記二

如何查找大小為500K到1000K之間的文件find / -type f -size 500k -and -size -1000k 僅列出當前目錄下的文件名.find ./ -type f -maxdepth 1 -exec basename {} /; 讓主機不響應pingecho 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all若想恢復就用echo 0 > /proc/sys/…

Shell腳本——入門

shell腳本編程需要注意以下幾個事項&#xff1a; shell腳本名稱命名一般為英文的大寫、小寫&#xff1b;不能使用特殊符號、空格來命名&#xff1b;shell腳本后綴以.sh結尾&#xff1b;不建議shell命名為純數字&#xff0c;一般以腳本功能命名&#xff1b;shell腳本內容首行需以…

Linux下C開發環境的構成和安裝

本文介紹了Linux的C開發環境的構成和安裝&#xff0c;使讀者對Linux的C開發環境能有初步的了解。 你了解Linux嗎&#xff1f;相信現在越來越多的人會說“是”的。那么你了解到何種程度呢&#xff1f;不可否認&#xff0c;目前決大多 數的Linux用戶對Linux的了解還處于比較低級的…

Shell腳本——變量

變量是任何一種編程語言都必不可少的組成部分&#xff0c;變量用來存放各種數據。腳本語言在定義變量時通常不需要指明類型&#xff0c;直接賦值就可以&#xff0c;Shell 變量也遵循這個規則 在 Bash shell 中&#xff0c;每一個變量的值都是字符串&#xff0c;無論你給變量賦值…

基礎環境安裝腳本

#/bin/bash # Check if user is root if [ $(id -u) ! "0" ]; thenecho "Error: You must be root to run this script, please use root"exit 1 fiecho "安裝環境包"if [ -f /etc/yum.repos.d/rhel-source.repo ];thenrm -r /etc/yum.repos.d…

Apache 虛擬主機的配置[Ubuntu]

基本配置 我們都知道&#xff0c;如果我們想在單臺機器上設置多個域名或主機名時&#xff0c;我們就要用到基于名稱的虛擬主機了。那么要如何進行設置呢&#xff1f;這就是本 HowTo 想解決的問題了。在 Ubuntu 的 /etc/apache2/ 目錄下有個 Apache2 的主配置文件 apache2.conf…

Shell腳本——數字計算

Shell 和其它編程語言不同&#xff0c;Shell 不能直接進行算數運算&#xff0c;必須使用數學計算命令 要想讓數學計算發揮作用&#xff0c;必須使用數學計算命令&#xff0c;Shell 中常用的數學計算命令如下表所示。 運算操作符/運算命令說明(( ))用于整數運算&#xff0c;效率…

windows下最好的C++ IDE

1. 你是不是用慣了VC6.0&#xff1f;假如是&#xff0c;我現在推薦的IDE仍然是VC6.0的IDE。 2. 你是不是覺得VC6.0過時了&#xff0c;很多C語法它都不支持&#xff1f;我現在推薦的編譯器她在一直發展著&#xff0c;支持最新的C語法。 3. VC.net的界面弱智&#xff0c;而且包含…

Shell腳本——內置命令

一、內置命令 所謂 Shell 內置命令&#xff0c;就是由 Bash 自身提供的命令&#xff0c;而不是文件系統中的某個可執行文件。 例如&#xff0c;用于進入或者切換目錄的 cd 命令&#xff0c;雖然我們一直在使用它&#xff0c;但如果不加以注意很難意識到它與普通命令的性質是不…

Shell腳本——基礎語法

一、條件判斷 1.1、基本語法 [ condition ]&#xff08;注意condition前后要有空格&#xff09; 注意&#xff1a;條件非空即為true&#xff0c;[ abcdef ]返回true&#xff0c;[] 返回false。 1.2、常用判斷條件 兩個整數之間比較 字符串比較 -lt 小于&#xff08;less …