2007年6月17日 星期日

bash 學習筆記 - 流程控制

與其他程式語言相比,bash 當然有提供的流程控制的能力,分別是 if/else、for、case、select、while/until,以下分別介紹這些語法的使用方式。

if/else

if/else 的語法如下:
if condition
then
statements
[elif condition
then statements...]
[else
statements]
fi

Exit Status

與其他程式語言(C、C++、Pascal....等等)不同的地方,在 condition 的部分,其他的程式語言是以 condition 為 True or False 來決定要執行哪一段程式;但在 bash 中卻不是如此!

在 bash 中,就必須按照 Linux 的運作方式來進行處理,因此 condition 是否成立取決於 command 的 exit status

而在 Linux 的所有指令,不論是由何種程式語言開發,執行完後都會回傳一個整數值給呼叫此程式的 process,此整數值即稱為 exit status;一般來說,「0」代表程式執行成功,而「1~255」則代表程式執行失敗。

有了 exit status 的概念後,if/else 的語法就可以修改如下:
if command ran successfully
then
normal processing
else
error processing
fi

Return

這邊要探討的是一般程式語言都有提供的 return 功能,其實說穿了,return N 即是 exit N(Exit Status 為N),不過比較不一樣的是,return 僅能用於 function 中,且要執行含有 function 的 shell script,必須透過 source 的方式來執行才有效。

總而言之,在 function 中使用 return 就跟在其他程式語言的方式差不多。

同時使用多個 Exit Status

或許有人會覺得,如果條件判斷中需要同時判斷兩個條件以上,要如何作呢? 在 bash 中當然也辦的到,就像其他程式語言一樣,bash 也可以針對判斷條件進行 &&(AND) 或是 ||(OR) 運算,來決定是否執行特定程式,以下用個簡單的範例說明:
filename=$1
word1=$2
word2=$3
if grep $word1 $filename || grep $word2 $filename
then
echo "$word1 or $word2 is in $filename."
fi

各種狀況的判斷

當然除了靠 Exit Status 外,還有許多不同的情況會發生,例如:字串比對、檔案屬性的檢查、數字比較....等等,以下逐一來進行介紹各種比對方式。

字串的比對:

Operator
成立條件
str1 = str2
這不多解釋了....
str1 != str2
這不多解釋了....
str1 < str2
這不多解釋了....
str1 > str2
這不多解釋了....
-n str1
當 str1 不為 null 時
-z str1
當 str1 為 null 時(長度為 0)

檔案屬性:
Operator
成立條件
-a file
檔案存在
-d file
檔案存在,並且是個目錄
-e file
檔案存在
-f file
檔案存在,且為一般檔案(非目錄或其他特殊型態)
-r file
目前的使用者有讀取檔案的權限
-s file
檔案存在且內容非空白
-w file
目前的使用者有寫入檔案的權限
-x file
目前的使用者有執行檔案(or 進入目錄)的權限
-N file
檔案自從上一次被讀取後,有被修改過
-O file
檔案的 owner 為目前的使用者
-G file
檔案的 group 為目前使用者所屬之 group
file1 -nt file2
file1 比 file2 的日期新
file1 -ot file2
file1 比 file2 的日期舊

如果使用上面的判斷時,最好加上「中括號」,變成類似以下的格式:
if [ condition ] && [ condition ]; then
當然也可以跟 Exit Status 結合,就會變成類似以下的格式:
if command && [ condition ]; then
以下來段簡單的程式(節錄自書):
# 檢查檔案是否不存在
if [ ! -e "$1" ]; then
echo "file $1 does not exist."
exit 1
fi

# 檢查是否為目錄
if [ -d "$1" ]; then
echo -n "$1 is a directory that you may "
if [ ! -x "$1" ]; then
echo -n "not "
fi
echo "search."
elif [ -f "$1" ]; then # 檢查是否為檔案
echo "$1 is a regular file."
else # 檢查是否為特殊型態的檔案
echo "$1 is a special type of file."
fi

# 檢查目前使用者是否為 owner
if [ -O "$1" ]; then
echo 'you own the file.'
else
echo 'you do not own the file.'
fi

# 檢查是否有讀取檔案的權限
if [ -r "$1" ]; then
echo 'you have read permission on the file.'
fi

# 檢查是否有寫入檔案的權限
if [ -w "$1" ]; then
echo 'you have write permission on the file.'
fi

# 檢查第一個參數是否有執行的權限,且非目錄
if [ -x "$1" -a ! -d "$1" ]; then
echo 'you have execute permission on the file.'
fi
當然,兩兩比較一定會有數字的比對,以下介紹數字比較的方式:
Keyword
效果
-lt
Less than (小於)
-le
Less then or equal (小於或等於)
-eq
Equal (等於)
-ge
Greater than or equal (大於或等於)
-gt
Greater than (大於)
-ne
Not equal (不等於)


for

for loop 在 bash 中當然也是不可欠缺的一員大將啦! 只是跟其他程式語言不同的地方,他並非以「數字」的方式直接指定 for loop 的程式要執行的次數,而是以一個串列的值(例如:1 3 4 6 8)去執行,有幾個值就執行幾次,因此格式大概如下:
for name [in list ]
do
statements that can use $name...
done
其實說穿了,使用方式就跟其他程式語言中的 for each 是差不多相同的,以下結合 if/else 判斷來個範例程式:
#!/bin/bash
IFS=":"
for dir in $PATH
do
if [ -z "$dir" ]; then
dir="."
fi

if ! [ -e "$dir" ]; then
echo "${dir} doesn't exist"
elif ! [ -d "${dir}" ]; then
echo "${dir} diesn't a directory"
else
ls -ld $dir
fi
done

另外再舉個例子,將 function、for、if/else 通通結合進來,下面這個程式的目的是以 recursive 的方式將參數中目錄底下的所有目錄列出,以下是程式內容:
echoDir() {
chrTab="${chrTab}t"

cd $1
for eachFilePath in $(ls)
do
if [ -d $eachFilePath ]; then
echo -e ${chrTab}${eachFilePath}
echoDir $eachFilePath
fi
done
cd ..

chrTab=${chrTab%'t'}
}

filePath=$1
chrTab=""

if [ -d $1 ]; then
cd $1
for eachFilePath in $(ls)
do
if [ -d $eachFilePath ]; then
echo $eachFilePath
echoDir $eachFilePath
fi
done
cd ..
fi
有興趣知道輸出結果的,可以 copy 回去自己試試看。


case

case 在 bash 中的用法其實就是跟 C、Java 等程式語言中的 switch case 差不多,以下是使用語法:
case expression in
pattern1 )
statements ;;
pattern2 )
statements ;;
...
esac
以下用個簡單程式來說明:
for filename in "$@"; do
case $filename in
*.jpg | *.png ) echo "may be jpg or png" ;;
*.tga ) echo "tga" ;;
*.xpm ) echo "xpm" ;;
* ) echo "can not identified file type."
esac
done


select

這個部分就跟一般的程式語言很不一樣了! 而且僅在 bash 1.14 版以後才有支援喔! 透過 select,可以很容易的產生出選單,以下是使用 select 的語法:
select name [in list ]
do
statements that can use $name...
done
透過 select 語法,可以將 list 中的內容變成一個個的數字選項供使用者選擇,並且有兩個變數需要注意到,分別是 $name 以及 $REPLY;其中 $name 變數的值為選項的內容,而 $REPLY 的值則是選項所代表的數字

當使用者輸入某選項的數字時,$name 與 $REPLY 就會存入相對應的值,但若是輸入一個非選項內的數字,則兩個變數的值都會變成 null,因此可以透過檢查 $name 變數值是否為 null 來判斷使用者有無輸入合法的選項。

以下用一個簡單的小程式來作範例:
#!/bin/bash

# 指定分隔 $PATH 的字元
IFS=":"
# 變數 PS3 儲存使用 select 時提示使用者的字串
PS3="which path you want to use? "
select mySelect in $PATH;
do
if [ $mySelect ]; then
echo "mySelect = ${mySelect}"
echo "REPLY = ${REPLY}"
else
echo "no such option!"
# 透過關鍵字「break」就可離開了!
break;
fi
done


# 以下是執行結果:
1) /usr/kerberos/sbin 4) /usr/local/bin 7) /usr/sbin
2) /usr/kerberos/bin 5) /sbin 8) /usr/bin
3) /usr/local/sbin 6) /bin 9) /root/bin
which path you want to use? 1
mySelect = /usr/kerberos/sbin
REPLY = 1
which path you want to use? 5
mySelect = /sbin
REPLY = 5
which path you want to use? 10
no such option!
至於 select 要如何應用在許許多多的管理工作上,就需要管理者發揮創意了!


while & until

在 while 與 until 的部分,也是跟一般程式語言中的用法差不多,以下分別介紹 while 與 until 的語法:

while
while condition do
statements... done
until
until command ; do statements... done
其實說穿了,while 與 until 不過就是一體兩面的用法而已,差別僅在於 while 判斷條件是否為 true,until 判斷條件是否為 false

以下用兩段程式來說明,以下兩段程式的作用是相同的,只是分別以 while 與 until 來撰寫:
until [ cp $1 $2 ];
do
echo 'Attempt to cpoy failed. waiting...'
sleep 5
done
while [ ! cp $1 $2 ];
do
echo 'Attempt to cpoy failed. waiting...'
sleep 5
done
這裡僅是簡單用法的介紹,其他就靠大家發揮了!

沒有留言:

張貼留言