* 概論
Shell Script 是一個類似 MS Windows 中 .bat 檔的東西,簡單的說,Shell Script 就是將一堆 shell 中的指令放在一個文字文件中來執行。因此,為了能寫出一個 shell Script,你必須先對 UNIX 指令有初步的認識。身為一個 UNIX 系統的管理者,一定要會使用 shell script 來使管理工作更加容易。
一般我們會將 Shell Script 的擴展名命名為 .sh,但並非一定要這麼做,這樣做只是為了要讓我們更容易管理這些檔案。在介紹如何 Shell Script 的內容之前,我們先來看如何寫出一個 Shell Script 並執行它。假設我們要寫一個名為 test.sh 的 Shell Script,首先用你習慣使用的文字編輯軟件來開一個文件名為 test.sh 內容如下:
- #!/bin/sh
- echo Hello world!!
1. 轉向輸入
2. 如果要輸入參數的話,第一種方式便不適用,可以改用這種方法。
3.你也可以改變 test.sh 的權限,將它變成可以獨立執行的檔案,這樣就可以只打 test.sh 來執行它:
在 Shell Script 中,你們可以使用 # 為註解,在 # 後面的字符串都將被視為註解而被式忽略。而分號 ; 則代表新的一行,例如打 ls;ls -d 代表二個指令。另外,我們可以使用變量、流程控制、甚至是副函式來使程序更加靈活。以下的各章節我們會詳細加以說明。
* 變量的使用
在 Shell Script 中,所有的變量都視為字符串,因此並不需要在定義變量前先定義變量類型。在 Shell 中定義和使用變量時有些許的差異。例如,我們定義一個變量 color 並令它的值為 red,接著使用 echo 來印出變量 color 的值:
在這裡,以 color=red 來定義變量 color 的值為 red,並以 echo $color 來印出 color 這一個變數。在定義變量時,不必加 $,但是在使用它時,必須加上 $。請注意,在等號的二邊不可以有空白,否則將出現錯誤 ,系統會誤以為你要執行一個指令。
我們再介紹一個範例:
這裡我們定義了變量 docpath 的值為 /home/td/src/doc,並印出它。接著我們使用 ls 這個指令來印出變量 docpath 目錄中所有檔案。再以 ls $docpath/*.txt 來印出 /home/td/src/doc/ 目錄下所有擴展名為 .txt 的檔案。
我們再來看一個例子,說明如何使用變量來定義變量:
另外,我們也可以使用指令輸出成為變量,請注意這裡使用的二個 ` 是位於鍵盤左上角的 ` ,在 shell script 中,使用 ` 包起來的代表執行該指令:
如果在變量之後有其它字符串時,要使用下列方式來使用變量:
這裡雙引號中的字將會被程序解讀,如果是使用單引號將直接印出 $light 而非 dark。
經由上面幾個簡單的例子,相信您對變量的使用已有初步的認識。另外有一些我們必須注意的事情:
我們可以看到上面各個執行結果不大相同。在 Shell Script 中,雙引號 " 內容中的特殊字符不會被忽略,而單引號中的所有特殊字符將被忽略。另外,\ 之後的一個字符將被視為普通字符串。如果您希望使用者能在程序執行到一半時輸入一個變量的值,您可以使用 read 這個指令。請看以下的範例:
- #!/bin/sh
- printf "Please input your name:"
- read Name
- echo "Your name is $Name"
您可以看到變量 Name 已被設為您所輸入的字符串了。
* 程序會自動定義的變量
在執行 Shell Script 時,程序會自動產生一些變量:
以下我們舉幾個例子來說明:
上面例子中的第一行是 ls,我們可以看到存在一個目錄 /home,接者 echo $? 時,出現 0 表示上一次的命令正常結束。接著我們 ls 一個不存在的目錄,再看 $? 這個變量變成 2,表示上一次執行離開的結果不正常。最後一個 echo $? 所得到的結果是 0,因為上一次執行 echo 正常顯示 2。
如果寫一個文件名為 abc.sh,內容如下:
- #!/bin/sh
- echo $#: $1 $2 $3 $4 $5 $6 $7 $8 $9
- echo $@
上面最後二行即為執行結果。我們可以看到 $# 即為參數的個數,而 $1, $2, $3...分別代表了輸入的參數 "a", "b c d", "e", "f",而最後的 $@ 則是所有參數。
* 系統內定的標准變量
你可以使用 set 這個指令來看目前系統中內定了哪些參數。一般而言會有 $HOME, $SHELL, $USER, $PATH 等。
* 空變量的處理
如果程序執行時,有一個變量的值尚未被給定,你可以利用下列方式來設定對於這種情形提出警告:
在 set -u 之後,如果變量尚未設定,則會提出警告。你也可以利用下列的方式來處理一些空變量及變量的代換:
我們以下面的例子來說明:
上面的例子中,變數 $name 並未被取代,而下面的例子中,$name 將被取代:
* 運算符號 : 四則運算
在 shell 中的四則運算必須使用 expr 這個指令來輔助。因為這是一個指令,所以如果要將結果指定給變量,必須使用 ` 包起來。請注意,在 + - * / 的二邊都有空白,如果沒有空白將產生錯誤:
還有一個要特別注意的是乘號 * 在用 expr 運算時,不可只寫 *。因為 * 有其它意義,所以要使用 \* 來代表。另外,也可以用 % 來求余數。
我們再列出更多使用 expr 指令的方式,下列表中為可以放在指令 expr 之後的表達式。有的符號有特殊意義,必須以 \ 將它的特殊意義去除,例如 \*,否則必須用單引號將它括起來,如 '*':
我們針對比較復雜的文字處理部份再加以舉例:
上面執行 tty 的結果是 /dev/pts/1,而在 expr 中,在 : 右側的表達式中,先找 .* 表示0個或一個以上任何字符,並藉由 \(.\) 取出最後一個字符。除了使用 expr 外,我們還可以使用下列這種特殊語法:
我們可以使用 $(()) 將表達式放在括號中,即可達到運算的功能。
* 運算符號 : 簡單的條件判斷
最簡單的條件判斷是以 && 及 || 這二個符號來表示。
* 以 test 來比較字符串及數字
我們說過 Shell Script 是一堆指令的組合,所以在比較字符串及數字時一樣是經由系統指令來達成。這裡我們使用 test 及 [ 來做運算,運算所傳回的結果是真 (true) 或假 ( false)。我們可以將它應用在條件判斷上。test 和 [ 都是一個指令,我們可以使用 test 並在其後加上下表中的參數來判斷真假。或者也可以使用 [ 表達式 ] 來替代 test,要注意的是 [ ] 中的空白間隔。
我們舉例來說明:
* 以 test 來處理檔案
我們也可以使用 test 及 [ 來判斷一個檔案的類型。下表中為其參數:
我們舉例來說明:
第一個指令測試 /bin 是否存在,而且是一個目錄,如果是則執行 echo 傳回一個字符串。第二個指令是測試 /etc/motd 是否可以被讀取,如果是則執行 echo 傳回一個字符串。
* 內建指令
在 Shell 中有一些內建的指令,這些內建的指令如流程控制及 cd 等指令是 Shell 中的必備元素。另外還有一些為了提高執行效率的指令,如 test、echo 等。有的內建指令在系統中也有同樣名稱不同版本的相同指令,但是如 test、echo 等在執行時會偽裝成是在 /bin 中的指令。
在寫 shell script 時,要注意指令是否存在。下列即為常見的內建指令:
指令 | 說明
exit |
. file |
echo |
pwd |
read var ... |
readonly [var..] |
return [n] |
set |
wait [n] |
exec command |
export [var] |
eval command |
* 流程控制
- if 的條件判斷
範例一:
- #!/bin/sh
- if test -r /etc/motd
- then cat /etc/motd
- else echo "There is not motd or file is not readable"
- fi
範例二:test.sh
- #!/bin/sh
- if [ $1 -gt 5 ]
- then echo " $1 is bigger then 5"
- elif [ $1 -ge 0 ]
- then echo " $1 is between 5 and 0. "
- else echo "$1 is less then 0."
- fi
說明:執行 ./test.sh 3,表示輸入一個參數 3。test.sh 檔案的內容表示依輸入的參數判斷參數大於 5 或介於 5 和 0 的中間,或者是小於 0。
- while 及 until 循環
範例一:
- #!/bin/sh
- i=1
- while [ $i -le 5 ]
- do
- echo $i
- i=`expr $i + 1`
- done
說明:首先令變量 i=1,接著在循環中當 i 小於等於 5 時就印出 i 的值,每印一次 i 就加 1。直到 i 大於 5 才停止。
範例二:
- #!/bin/sh
- i=1
- until [ $i -gt 5 ]
- do
- echo $i
- i=`expr $i + 1`
- done
說明:首先令變量 i=1,接著循環會判斷,一直執行到 i 大於 5 才停止。每跑一次循環就印出 i 的值,每印一次 i 就加 1。注意 while 和 until 的判斷式中,一個是 -le ,一個是 -gt。
- for 循環
範例一:color1.sh
- #!/bin/sh
- for color in blue red green
- do
- echo $color
- done
說明:這個檔案 color1.sh 中,會在每一次循環中將關鍵詞 in 後面的字符串分配給變量 color,然後印出變量 color。關鍵詞 in 讓我們可以依序設定一些值並指派給變量,然而,我們也可以不使用關鍵詞 in。如果沒有關鍵詞 in ,程序會自動讀取輸入的參數,並依序指派給 for 之後的變量。請看範例二。
範例二:color2.sh
- #!/bin/sh
- for color
- do
- echo $color
- done
說明:在 color2.sh 這個檔中,for 循環沒有使用 in 這個關鍵詞。但我們在執行它時輸入三個參數,循環會自動將輸入的參數指派給 for 之後的變量 color,並印出它。
* case 判斷
範例:num.sh
- for num
- do
- case $num in
- 0|1|2|3) echo $num is between 0~3;;
- 4|5|6|7) echo $num is between 4~7;;
- 8|9) echo $num is 8 or 9;;
- *) echo $num is not on my list;;
- esac
- done
說明:這個程序是用來判斷輸入的參數大小。for 循環會將每一個輸入的參數指定給變量 num,而在 case 中,判斷變量 num 的內容符合哪一個條件,同一個條件中的每個字用 | 分開。如果未符上面的條件則一定會符合最後一個條件 * 。每一個要執行的 list 是以 ;; 做結尾,如果有多行 list,只要在最後一行加上一個 ;; 即可。
* 函式的運用
在 Shell Script 中也可以使用函式 (function) 來使用程序模塊化。
函式有幾個要注意的地方:
範例:fun_s1.sh
- #! /bin/sh
- ERRLOG=$1
- ok ( )
- {
- read ans
- case $ans in
- [yY]*) return 0;;
- *) return 1;;
- esac
- }
- errexit ( )
- {
- echo $1
- date >> $ERRLOG
- echo $1 >> $ERRLOG
- exit
- }
- echo -n "Test errexit function [y/n] "
- ok && errexit "Testing the errexit function"
- echo Normal termination
說明:
這個程序中有二個函式:errexit 及 ok。第一行定義要將 log 檔存在傳給這個 Shell Script 的第一個參數。接著是二個函式,之後印出一行字,echo -n 表示印出字後游標不換行。然後再執行 ok 這個函式,如果 ok 函式執行成功則再執行 errexit 函式,並傳給 errexit 函式一個字符串,最後再印出一個字符串。
在 ok 函式中,使用 read 指令來讀入一個參數並指派給變數 ans。接著判斷使用者輸入的值是否為 Y 或 y,如果是則傳回 1 代表沒有成功執行,如果不是則傳回 0 代表成功執行函式 ok。
如果 ok 函式傳回 1 便不會執行 errexit 函式。如果是 0 則在 errexit 函式中,會先印出要傳給 errexit 的參數 " Testing the errexit function",並記錄在指定的檔案中。
* 參考資料
1. http://man.ddvip.com/os/freebsd_book_chs/ch24.htm
沒有留言:
張貼留言