Unix系列shell程序編寫(中)
3>在Shell中使用數(shù)據(jù)變量
用戶可以在Shell中使用數(shù)據(jù)變量,例如ba.sh程序:
cd/usr/icewalk ls|cpio -o > /dev/fd0
該程序中要備份的目錄為一常量,即該程序只能用來備份一個(gè)目錄。若在該程序中使用變量,則會(huì)使其更通用:
workdir=$1 cd $workdir ls * |cpio -o > /dev/fd0
通過這一改變,用戶可以使用程序備份變量$workdir指定的目錄。例如我們要備份/home/www的內(nèi)容,只要運(yùn)行ba.sh /home/www即可實(shí)現(xiàn)。(若不明白 $1,下面將詳細(xì)介紹shell參數(shù)的傳遞,$1代表本sh程序-ba.sh的第一個(gè)參數(shù))
4>在Shell程序中加上注釋
為了增加程序的可讀性,我們提倡加入注釋。在Shell程序中注釋將以'#'號(hào)開始。當(dāng)Shell解釋到'#'時(shí),會(huì)認(rèn)為從'#'號(hào)起一直到該行行尾為注釋。
5>對(duì)Shell變量進(jìn)行算術(shù)運(yùn)算
高級(jí)語言中變量是具有類型的,即變量將被限制為某一數(shù)據(jù)類型,如整數(shù)或字符類型。Shell變量通常按字符進(jìn)行存儲(chǔ),為了對(duì)Shell變量進(jìn)行算術(shù)運(yùn)算,必須使用expr命令。
expr命令將把一個(gè)算術(shù)表達(dá)式作為參數(shù),通常形式如下:
expr [數(shù)字] [操作符] [數(shù)字]
由于Shell是按字符形式存儲(chǔ)變量的,所以用戶必須保證參加算術(shù)運(yùn)算的操作數(shù)必須為數(shù)值。下面是有效的算術(shù)操作符:
+ 兩個(gè)整數(shù)相加 - 第一個(gè)數(shù)減去第二個(gè)數(shù) * 兩整數(shù)相乘 / 第一個(gè)整數(shù)除以第二個(gè)整數(shù) % 兩整數(shù)相除,取余數(shù) 例如: $expr 2 + 1 結(jié)果顯示:3 $expr 5 - 3 結(jié)果顯示:2若expr的一個(gè)參數(shù)是變量,那么在表達(dá)式計(jì)算之前用變量值替換變量名。 $int=3 $expr $int + 4 結(jié)果顯示:7 用戶不能單純使用'*'做乘法,若輸入: $expr 4*5 系統(tǒng)將會(huì)報(bào)錯(cuò),因?yàn)镾hell看到'*'將會(huì)首先進(jìn)行文件名替換。正確形式為: $expr 4 * 5 結(jié)果顯示:20 多個(gè)算術(shù)表達(dá)式可以組合在一起,例如: $expr 5 + 7 / 3 結(jié)果顯示:7 運(yùn)算次序是先乘除后加減,若要改變運(yùn)算次序,必須使用'`'號(hào),如: $int=`expr 5 + 7` $expr $int/3 結(jié)果顯示:4 或者: $expr `expr 5+7`/3 結(jié)果顯示:4
6>向Shell程序傳遞參數(shù)
一個(gè)程序可以使用兩種方法獲得輸入數(shù)據(jù)。一是執(zhí)行時(shí)使用參數(shù)。另一種方法是交互式地獲得數(shù)據(jù)。vi編輯程序可以通過交互式的方法獲得數(shù)據(jù),而ls和expr則從參數(shù)中取得數(shù)據(jù)。以上兩種方法Shell程序都可以使用。在'交互式讀入數(shù)據(jù)'一節(jié)中將介紹Shell程序通過交互式的方法獲得參數(shù)。
通過命令行給Shell程序傳遞參數(shù)可以擴(kuò)大程序的用途。以前面提到的ba.sh程序?yàn)槔? $cat >re.sh cd $workdir cpio -i < /dev/fd0 ^d
程序re.sh恢復(fù)了ba.sh程序備份的所有文件。若只從軟盤上恢復(fù)一個(gè)指定的文件,可以用該文件名作為參數(shù),傳遞給Shell程序re.sh:
程序改寫如下: $cat >re2.sh cd $workdir cpio -i $1 < /dev/fd0 ^d
用戶可以指定要恢復(fù)的文件,例如fname
$re2.sh fname
此時(shí)文件fname作為第一個(gè)位置參數(shù)傳遞給re2.sh,re2.sh的缺點(diǎn)是要恢復(fù)兩個(gè)或多個(gè)文件要重復(fù)運(yùn)行,我們可以用$*變量傳遞不確定的參數(shù)給程序:
$cat >re3.sh cd $workdir cpio -i $* < /dev/fd0 ^d
我們就可以恢復(fù)多個(gè)文件,例如fname1,fname2,fname3 $re3.sh fname1 fname2 fname3 (以上程序re.sh,re2.sh,re3.sh,假設(shè)用戶已經(jīng)chmod了可執(zhí)行權(quán)利)
因?yàn)闆]有賦值的變量可以作為NULL看待,所以若是程序re3.sh在執(zhí)行時(shí)候沒賦予參數(shù),那么一個(gè)空值將被插入到cpio命令中。該命令將恢復(fù)所有保存的文件。 條件判斷語句
條件判斷語句是程序設(shè)計(jì)語言中十分重要的語句,該語句的含義是當(dāng)某一條件滿足時(shí),執(zhí)行指定的一組命令。
1>if - then語句
格式: if command1 then command2 command3 fi---(if 語句結(jié)束) command4
每個(gè)程序或命令執(zhí)行結(jié)束后都有一個(gè)返回的狀態(tài),用戶可以用Shell變量$?獲得這一狀態(tài)。if語句檢查前面命令執(zhí)行的返回狀態(tài),若該命令成功執(zhí)行,那么在then和fi之間的命令都將被執(zhí)行。在上面的命令序列中,command1和command4總要執(zhí)行。若command1成功執(zhí)行,command2和command3也將執(zhí)行。
請(qǐng)看下面程序: #unload -program to backup and remove files cd $1 ls -a | cpio -o > /dev/mnt0 rm *
該程序在備份資料后,刪除檔案,但當(dāng)cpio命令不能成功執(zhí)行時(shí),rm命令還是把資料刪除了,我們可不希望這樣,為了避免此情況,可以用if - then語句: #--卸載和判斷刪除程序
cd $1 if ls -a | cpio > /dev/mnt0 then rm * fi 上面程序在cpio執(zhí)行成功后才刪除檔案
同時(shí),若執(zhí)行沒有成功,我們希望得到提示,sh中的echo命令可以向用戶顯示消息,并顯示后換行,上面程序可以寫成: #--卸載和判斷刪除程序 cd $1 if ls -a | cpio > /dev/mnt0 then echo '正刪除文件資料... ...' rm * fi
echo命令可以使用一些特殊的逃逸字符進(jìn)行格式化輸出,下面是這些字符及其含義:
bBackspace c顯示后不換行 f在終端上屏幕的開始處顯示 n換行 r回車 t制表符 v垂直制表符 反斜框 nnn 用1,2或3位8進(jìn)制整數(shù)表示一個(gè)ASCII碼字符
2>if - then - else語句
不用多說它的作用,別的高級(jí)語言中都有,格式為: if command1 then command2 command3 else command4 command5 fi
在此結(jié)構(gòu)中,command1中是先執(zhí)行,當(dāng)command1成功執(zhí)行時(shí),將執(zhí)行command2和command3,否則執(zhí)行command4和command5
注意看下面程序: #備份程序 cd $1 if ls -a |cpio -o > /dev/mnt0 then echo '刪除源資料... ...' rm * else echo '磁帶備份失敗!' fi
3>test命令進(jìn)行條件測(cè)試
if語句可以通過測(cè)試命令執(zhí)行的返回狀態(tài)來控制命令的執(zhí)行,若要測(cè)試其他條件,在bsh中可以使用test命令。該命令檢測(cè)某一條件,當(dāng)條件為真時(shí)返回0,否則返回非0值。test命令可以使Shell程序中的if語句象其他程序語言中的條件判斷語句一樣,具有很強(qiáng)的功能。
test命令的使用方法為: test condition 可測(cè)試的條件分為4類: 1)測(cè)試兩個(gè)字符串之間的關(guān)系。 2)測(cè)試兩個(gè)整數(shù)之間關(guān)系。 3)測(cè)試文件是否存在或是否具有某種狀態(tài)或?qū)傩浴? 4)測(cè)試多個(gè)條件的與(and)或(or)組合。
1、條件語句>>test語句
1>測(cè)試字符串間的關(guān)系
bsh把所有的命令行和變量都看作字符串。一些命令如expr和test可以把字符當(dāng)作數(shù)字進(jìn)行操作。
同樣任何數(shù)字也可以作為字符串進(jìn)行操作。
用戶可以比較兩個(gè)字符串相等或不等,也可以測(cè)試一個(gè)串是否賦了值。有關(guān)串的操作符如下: str1 = str2當(dāng)兩個(gè)串有相同內(nèi)容、長度時(shí)為真 str1 != str2 當(dāng)串str1和str2不等時(shí)為真 -n str1 當(dāng)串的長度大于0時(shí)為真(串非空) -z str1 當(dāng)串的長度為0時(shí)為真(空串) str1 當(dāng)串str1為非空時(shí)為真
不但Shell程序可以使用test進(jìn)行條件判斷,test命令也可以獨(dú)立執(zhí)行,如:
$str1=abcd $test $str1 = abcd $echo $? 結(jié)果顯示:0
與上例中第一行賦值語句中的等號(hào)不同,test命令中的等號(hào)兩邊必須要有空格。本例test命令共有3個(gè)參數(shù)。注意兩個(gè)串相等必須是長度和內(nèi)容都相等。
$str1='abcd ' $test '$str1' = abcd $echo $? 結(jié)果顯示:1
上面str1包含5個(gè)字符,其中最后一個(gè)為空格符。而test命令中的另一個(gè)串只有4個(gè)字符,所以兩串不等,test返回1。
不帶任何操作符和使用-n操作符測(cè)試一個(gè)串結(jié)果是一樣的,例如:
$str1=abce $test $str1 $echo $? 結(jié)果顯示:0 $test -n $str1 $echo $? 結(jié)果顯示:0
但是,上面兩條命令也有一點(diǎn)差別,反映出了使用test命令潛在的問題,請(qǐng)看下例:
$str1=' ' $test $str1 $echo $? 結(jié)果顯示:1 $test -n '$str1' $echo $? 結(jié)果顯示:0 $test -n $str1 結(jié)果顯示:test:argument expected
上例中,第一次測(cè)試為假因?yàn)镾hell在執(zhí)行命令行之前首先要進(jìn)行變量替換,即把$str1換成空格,然后shell又將命令行上的空格刪除,故test命令測(cè)試到的為空串。而在第二次測(cè)試中,變量替換后空格位于括號(hào)內(nèi),故不會(huì)被刪除,test測(cè)試到的是一個(gè)包含空格的串,在第三次測(cè)試中,shell把空格刪除,只把-n傳個(gè)test命令,所以顯示參數(shù)錯(cuò)。
2>測(cè)試兩個(gè)整數(shù)之間關(guān)系
test命令與expr命令一樣,也可以把字符轉(zhuǎn)變成整數(shù),然后對(duì)其操作。test命令對(duì)兩個(gè)數(shù)進(jìn)行比較,使用的操作符如下:
int1 -eq int2兩數(shù)相等為真 int1 -ne int2兩數(shù)不等為真 int1 -gt int2int1大于int2為真 int1 -ge int2int1大于等于int2為真 int1 -lt int2int1小于int2為真 int1 -le int2int1小于等于int2為真
下面的例子反映了字符串比較與數(shù)字比較的不同:
$str1=1234 $str2=01234 $test $str1 = $str2 $echo $? 結(jié)果顯示:1 $test $str1 -eq $str2 $echo $? 結(jié)果顯示:0
3>有關(guān)文件的測(cè)試
使用test進(jìn)行的第三類測(cè)試是測(cè)試文件的狀態(tài),用戶可以測(cè)試文件是否存在,是否可寫以及其他文件屬性。下面是文件測(cè)試時(shí)使用的選項(xiàng)。注意只有文件存在時(shí),才有可能為真。
-r file 用戶可讀為真 -w file 用戶可寫為真 -x file 用戶可執(zhí)行為真 -f file 文件為正規(guī)文件為真 -d file 文件為目錄為真 -c file 文件為字符特殊文件為真 -b file 文件為塊特殊文件為真 -s file 文件大小非0時(shí)為真 -t file 當(dāng)文件描述符(默認(rèn)為1)指定的設(shè)備為終端時(shí)為真 4>復(fù)雜的條件測(cè)試(and 、or 、not) -a 與 -o 或 !非 就是組合條件了,任何高級(jí)語言中都有的(NOT 、AND 、OR),例如: $test -r em.null -a -s em.null $echo $? 結(jié)果顯示:1 說明了em.null并不是可讀并且非空的文件
5>另一種執(zhí)行test的方法
bsh中還有另一種執(zhí)行test命令的方法,就是把測(cè)試條件放到一對(duì)[ ]中,例如: $int1=4 $[ $int1 -gt 2 ] $echo $? 結(jié)果顯示:0 要注意在[ 的后面和 ]符號(hào)的前面要有一個(gè)空格。 下面我們用test命令寫個(gè)簡(jiǎn)單但比較完善的程序:
#-- 備份程序 #-- 檢查參數(shù) if [ $# -ne 1 ] then
echo '請(qǐng)?jiān)诔绦蛎竺嬷赋鲆獋浞菸募谀夸?' exit 1 fi #-- 檢查目錄名是否有效 if [ !-d '$1' ] then echo '$1 不是一個(gè)目錄!' exit 2 fi cd $1 ls -a | cpio -o >/dev/mnt0 if [ $? -eq 0 ] then rm * else echo 'cpio執(zhí)行不成功!備份失敗...' exit 3 fi
6>空命令
在Bsh中用 : 代表空命令,就是充個(gè)數(shù),什么都不做
7>嵌套if語句和elif結(jié)構(gòu)
檢查條件1 A:當(dāng)條件1為真,則執(zhí)行一部分操作 B:若條件1為假,檢查條件2 1)若條件2為真,執(zhí)行另外一部分操作 2)若條件2為假,檢查條件3 3)若條件3為真,執(zhí)行其他一部分操作 語法如下: if command then command else if command then command else if command then command fi fi fi
8>elif語句
嵌套if語句有時(shí)會(huì)給用戶帶來混亂,特別是什么時(shí)候fi語句很難判斷。因此Bourne Shell又提供了elif語句。elif是else-if的縮寫,它表示是if語句的繼續(xù)。格式為:
if command then command elif command then command elif command then command fi
上面介紹的嵌套if語句和elif語句完成相同的功能,用戶可以根據(jù)自己的喜好選擇一種使用。
9>case語句
前面說的elif語句替代if-then-else語句,但有時(shí)在編程時(shí)還會(huì)遇到對(duì)同一變量進(jìn)行多次的測(cè)試,該情況可以用多個(gè)elif語句實(shí)現(xiàn),但還有一種更簡(jiǎn)單的方法就是用case語句。
case語句不但取代了多個(gè)elif和then語句,還可以用變量值對(duì)多個(gè)模式進(jìn)行匹配,當(dāng)某個(gè)模式與變量值匹配后,其后的一系列命令將被執(zhí)行,下面是case語句使用的語句。
case value in pattem 1) command command pattem 2) command command .... pattem) command; esac
case語句只執(zhí)行其中的一組命令,當(dāng)變量值與多個(gè)模式相匹配時(shí),只有第一個(gè)匹配的模式對(duì)應(yīng)的命令被執(zhí)行。';;'表示該模式對(duì)應(yīng)的命令部分程序。
通過學(xué)習(xí)下面的read語句,我們們?cè)倥e例子說明case語句的用法。
10>read語句
Shell程序不但可以通過命令行參數(shù)得到輸入數(shù)據(jù),還可以使用read命令提示用戶輸入數(shù)據(jù),其語法格式為:
read var1 var2... ...varn
當(dāng)Bsh遇到一個(gè)read語句時(shí),在標(biāo)準(zhǔn)輸入文件中讀取數(shù)據(jù)直到一個(gè)換行符。此時(shí)Shell在解釋輸入行時(shí),不進(jìn)行文件名或變量的替換,只是簡(jiǎn)單地刪除多余的空格。然后Shell將輸入行的第一個(gè)字的內(nèi)容給變量1,第二個(gè)給變量2,直到所有變量都賦上值或是輸入行為空。若輸入行中字的個(gè)數(shù)超過變量個(gè)數(shù),Shell將把輸入行中剩余的所有字的內(nèi)容都賦給最后一個(gè)變量。當(dāng)變量個(gè)數(shù)多于輸入行字的個(gè)數(shù)時(shí)候,多于的變量將賦一個(gè)空值。輸入行的每一個(gè)字是由空格分隔的一個(gè)字母和數(shù)字組成的字符串。
$read var1 var2 var3 輸入:Hello my frIEnd $echo $var1 $var2 $var3 結(jié)果顯示:Hello my friend $echo $var2 結(jié)果顯示:my
下面用個(gè)read和case的例子結(jié)束本部分的學(xué)習(xí):
#--交互式備份,恢復(fù)程序 echo '輸入要備份文件所在目錄:c' read WORKDIR if [ !-d $WORKDIR ] then echo 'Sorry,$WORKDIR is not a Directory' exit 1 fi cd $WORKDIR echo '輸入選擇:' echo _ echo '1.恢復(fù)到 $WORKDIR' echo '2.備份 $WORKDIR' echo '0.退出' echo echo 'c' read CHOICE case '$CHOICE' in 1)echo '恢復(fù)中... ...' cpio -i < /dev/mnt0 2)echo '備份中... ...' ls | cpio -o > /dev/mnt0 0)exit 1 *)exit 1 esac if [ $? -ne 0 ] then echo '程序運(yùn)行中出現(xiàn)錯(cuò)誤!' else echo '操作成功!' fi 在上面代碼中,'*'定義了其他模式下不匹配時(shí)的默認(rèn)操作。 循環(huán)語句
前面介紹的程序和所學(xué)的語句都是從頭到尾成一條主線下來,或是成分支結(jié)構(gòu),在日常管理Unix的過程中,經(jīng)常要重復(fù)的做一些操作,處理批量的問題,這就涉及到了循環(huán)結(jié)構(gòu),同高級(jí)語言相似,UNIX的Shell也提供了強(qiáng)大的循環(huán)處理語句。
Bsh語言中有三種循環(huán)語句-while循環(huán)、until循環(huán)、for循環(huán),下面通過具體的例子分別介紹這三種結(jié)構(gòu)。
While循環(huán)
在while循環(huán)語句中,當(dāng)某一條件為真時(shí),執(zhí)行指定的命令。語句的結(jié)構(gòu)如下:
while command do command command … … done
示例代碼如下:
#測(cè)試while循環(huán)小程序
x_t=1 while [ $x_t -lt 5 ] do mm=` expr $x_t * $int `#注意''的作用 echo '$mm' x_t=` expr $x_t + 1 ` #注意expr的用法 done echo 'THE WHILE IS END!n'
程序的執(zhí)行結(jié)果如下: 1 4 9 16 THE WHILE IS END
在上述程序中,當(dāng)變量x_t的值小于5的時(shí)候,執(zhí)行while循環(huán)中的語句。在第五次循環(huán)時(shí), [ $x_t-lt5]命令返回非零值,于是程序執(zhí)行done后面的代碼。 現(xiàn)在利用while循環(huán),可以改進(jìn)我們?cè)缧r(shí)候用的備份數(shù)據(jù)的例子,當(dāng)用戶指定的目錄備份完畢后,使用while循環(huán)使程序執(zhí)行一次可以備份多個(gè)用戶指定的目錄。代碼如下:
echo '歡迎使用備份小程序'
ANS=Y while [ $ANS = Y -o $ANS = y ] do echo _ #讀目錄名 echo '輸入要備份的目錄名:c' read DIR if [ ! -d $DIR ] then echo '$DIR不是一個(gè)目錄!' exit 1 fi cd $DIR echo '請(qǐng)選擇:' echo _ echo '1 恢復(fù)數(shù)據(jù)到 $DIR' echo '2 備份$DIR的數(shù)據(jù)' echo echo '請(qǐng)選擇:c' read CHOICE case '$CHOICE' in 1) echo '恢復(fù)中… …' cpio -i 2) echo '備份中… …' cpio -o >/dev/rmt0 *) echo '選擇無效' esac if [ $? -ne 0 ] then echo 'cpio執(zhí)行過程中出現(xiàn)問題' exit 2 fi echo '繼續(xù)別的目錄嗎?(Y/y)c' read ANS done
在程序開始,我們給變量ANS符值為Y,根據(jù)whlie的判斷條件,程序進(jìn)入while循環(huán),執(zhí)行do-done中的語句,每次循環(huán)都要求用戶輸入ANS的值用來判斷是否進(jìn)行下次重復(fù)執(zhí)行do-done中的語句。如果用戶輸入的條件不滿足while語句條件,循環(huán)結(jié)束,程序執(zhí)行done后面的語句。
