學習 Shell Scripts
本文資料主要針對 Fedora Core 4 的系統進行說明, Fedora Core 1 主要是由 Red Hat Linux 9 改版而來, 這個 Red Hat Linux 9 並不是當前大家聽到的 RHEL 喔!那是在 RHEL 出現之前的產品,基本上是在 2003 年以前的作品了!Fedora Core 4 則是在 2005 年 6 月份釋出,使用的核心是 2.6.11 版,當時是很紅的一個作品!只是生命週期太短,所以用這個 Fedora 系列來介紹 Server, 當時的決定確實有點莫名其妙了...
建議您前往本站查詢最新版本的 Linux distribution 文章來閱讀,比較不會浪費時間。那為何還需要編輯 Fedora Core 4 的資料呢? 鳥哥只想要做個自己曾經撰寫過的文件內容保存而已囉! ^_^!最新文章請前往鳥站首頁查閱囉!
如果您真的很想要走資訊這條路,並且想要好好的管理好屬於您的主機,那麼,別說鳥哥不告訴您, Shell Scripts 真的是必須要學習的一項課題呢!基本上, shell script 有點像是早期的批次檔, 亦即是將一些指令彙整起來一次執行,但是 Shell script 擁有更強大的功能,那就是, 他可以進行類似程式 (program) 的撰寫,並且,不需要經過編譯 (compiler) 就能夠執行, 真的很方便。加上,我們可透過 shell script 來簡化我們日常的工作管理, 而且,整個 Linux 環境中,一些服務 (services) 的啟動都是透過 shell script 的, 如果您對於 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 這個目錄內, 比較好管理啦!上面的寫法當中,我主要將整個程式的撰寫分成數段,大致是這樣:
[root@linux scripts]# sh sh01.sh
Hello World !
您會看到螢幕是這樣,而且應該還會聽到『咚』的一聲,為什麼呢?還記得前一章提到的
printf 吧?用 echo 接著那些特殊的按鍵也可以發生同樣的事情~
不過, echo 必須要加上 -e 的參數才行!
呵呵!在您寫完這個小 script 之後,您就可以大聲的說:『我也會寫程式了』!哈哈!
很簡單有趣吧~ ^_^[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 執行一下,你就能夠發現使用者自己輸入的變數可以被取用的哩! 很不錯吧!加油!
[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 吧! ^_^
[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 them! \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 』 也可以使用上面的方式來進行!基本上,鳥哥比較建議使用這樣的方式來進行運算:
[root@linux scripts]# nu=$((13%3)); echo $nu
1
這樣瞭解了吧?!多多學習與應用喔! ^_^
[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 |
[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"很有趣的例子吧!您可以自行再以其他的案例來撰寫一下可用的功能呢!
[root@linux ~]# [ -z "$HOME" ]
但使用 [] 要特別注意的是,在上述的每個元件中間都需要有空白鍵來分隔,假設我空白鍵使用『□』來表示,
那麼,在這些地方你都需要有空白鍵:[ "$HOME" == "$MAIL" ] [□"$HOME"□==□"$MAIL"□] ↑ ↑ ↑ ↑上面的例子在說明,兩個字串 $HOME 與 $MAIL 是否相同的意思,相當於 test $HOME = $MAIL 的意思啦! 而如果沒有空白分隔,例如 [$HOME==$MAIL] 時,我們的 bash 就會顯示錯誤訊息了!這可要很注意啊! 所以說,您最好要注意:
[root@linux ~]# name="VBird Tsai" [root@linux ~]# [ $name == "VBird" ] bash: [: too many arguments為什麼呢?因為 $name 如果沒有使用雙引號刮起來,那麼上面的判定式會變成:
[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 choice" && exit 0很有趣吧!利用這個字串判別的方法,我們就可以很輕鬆的將使用者想要進行的工作分門別類呢! 接下來,我們再來談一些其他有的沒有的東西吧!
[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 name is ==> $0" [ -n "$1" ] && echo "The 1st parameter is ==> $1" || exit 0 [ -n "$2" ] && echo "The 2nd parameter is ==> $2" || exit 0 [ -n "$3" ] && echo "The 3th parameter is ==> $3" || exit 0這支程式裡面鳥哥加上了一些控制式,亦即利用 && 及 || 來加以判斷 $1 ~ $3 是否存在? 若存在才顯示,若不存在就中斷~執行結果如下:
[root@linux scripts]# sh sh07.sh theone haha quot
The script name is ==> sh07.sh
The 1st paramter is ==> theone
The 2nd paramter is ==> haha
The 3th paramter is ==> quot
上面這7個例子都很簡單吧?幾乎都是利用 bash 的相關功能而已~
不難啦~底下我們就要使用條件判斷式來進行一些分別功能的設定了,好好瞧一瞧先~
if [ 條件判斷式 ]; then 當條件判斷式成立時,可以進行的指令工作內容; fi至於條件判斷式的判斷方法,與前一小節的介紹相同啊!較特別的是,如果我有多個條件要判別時, 除了 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 choice" && 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 choice" fi是否程式變得很簡單,而且依序判斷,可以避免掉重複判斷的狀況,這樣真的很容易設計程式的啦! ^_^ 好了,那麼如果我要偵測你所輸入的參數是否為 hello 呢 , 也就是說,如果我想要知道,你在程式後面所接的第一個參數 (就是 $1 啊!) 是否為 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 與相關網路服務的關係是:
[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) 。那麼,您是否可以撰寫一支小程式,用來『計算退伍日期還剩幾天?』也就是說:
[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 $變數名稱 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 的服務,我想要重新啟動這個服務,可以這樣做:
[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 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以上面的例子來說,我做了一個函數名稱為 printit ,所以,當我在後續的程式段裡面, 只要執行 printit 的話,就表示我的 shell script 要去執行『 function printit .... 』 裡面的那幾個程式段落囉! 當然囉,上面這個例子舉得太簡單了,所以您不會覺得 function 有什麼好厲害的, 不過,如果某些程式碼一再地在 script 當中重複時,這個 function 可就重要的多囉~ 不但可以簡化程式碼,而且可以做成類似『模組』的玩意兒,真的很棒啦!
[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 而已,所以,這裡看看即可~瞭解原理就好囉~ ^_^
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這種語法適合於數值方式的運算當中,在 for 後面的括號內的三串內容意義為:
[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 的變數內容在迴圈工作時:
[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呵呵!很有趣的例子吧~利用這種方式,您可以很輕易的來處理一些檔案的特性呢~ 我們迴圈就介紹到這裡了~其他更多的應用,就得視您的需求來玩囉~。
[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 參數,就可以知道問題是發生在哪一行上面了!
# 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 時, 如果是系統提供的,那麼利用這個檢查的方式,一定可以約略瞭解的啦! 加油的囉~ ^_^