命令test
或[
可以測(cè)試一個(gè)條件是否成立,如果測(cè)試結(jié)果為真,則該命令的Exit Status為0,如果測(cè)試結(jié)果為假,則命令的Exit Status為1(注意與C語(yǔ)言的邏輯表示正好相反)。例如測(cè)試兩個(gè)數(shù)的大小關(guān)系:
$ VAR=2$ test $VAR -gt 1$ echo $?0$ test $VAR -gt 3$ echo $?1$ [ $VAR -gt 3 ]$ echo $?1
雖然看起來(lái)很奇怪,但左方括號(hào)[
確實(shí)是一個(gè)命令的名字,傳給命令的各參數(shù)之間應(yīng)該用空格隔開(kāi),比如,$VAR
、-gt
、3
、]
是[
命令的四個(gè)參數(shù),它們之間必須用空格隔開(kāi)。命令test
或[
的參數(shù)形式是相同的,只不過(guò)test
命令不需要]
參數(shù)。以[
命令為例,常見(jiàn)的測(cè)試命令如下表所示:
表 31.2. 測(cè)試命令
[ -d DIR ] | 如果DIR 存在并且是一個(gè)目錄則為真 |
[ -f FILE ] | 如果FILE 存在且是一個(gè)普通文件則為真 |
[ -z STRING ] | 如果STRING 的長(zhǎng)度為零則為真 |
[ -n STRING ] | 如果STRING 的長(zhǎng)度非零則為真 |
[ STRING1 = STRING2 ] | 如果兩個(gè)字符串相同則為真 |
[ STRING1 != STRING2 ] | 如果字符串不相同則為真 |
[ ARG1 OP ARG2 ] | ARG1 和ARG2 應(yīng)該是整數(shù)或者取值為整數(shù)的變量,OP 是-eq (等于)-ne (不等于)-lt (小于)-le (小于等于)-gt (大于)-ge (大于等于)之中的一個(gè) |
和C語(yǔ)言類(lèi)似,測(cè)試條件之間還可以做與、或、非邏輯運(yùn)算:
表 31.3. 帶與、或、非的測(cè)試命令
[ ! EXPR ] | EXPR 可以是上表中的任意一種測(cè)試條件,!表示邏輯反 |
[ EXPR1 -a EXPR2 ] | EXPR1 和EXPR2 可以是上表中的任意一種測(cè)試條件,-a 表示邏輯與 |
[ EXPR1 -o EXPR2 ] | EXPR1 和EXPR2 可以是上表中的任意一種測(cè)試條件,-o 表示邏輯或 |
例如:
$ VAR=abc$ [ -d Desktop -a $VAR = 'abc' ]$ echo $?0
注意,如果上例中的$VAR
變量事先沒(méi)有定義,則被Shell展開(kāi)為空字符串,會(huì)造成測(cè)試條件的語(yǔ)法錯(cuò)誤(展開(kāi)為[ -d Desktop -a = 'abc' ]
),作為一種好的Shell編程習(xí)慣,應(yīng)該總是把變量取值放在雙引號(hào)之中(展開(kāi)為[ -d Desktop -a "" = 'abc' ]
):
$ unset VAR$ [ -d Desktop -a $VAR = 'abc' ]bash: [: too many arguments$ [ -d Desktop -a "$VAR" = 'abc' ]$ echo $?1
和C語(yǔ)言類(lèi)似,在Shell中用if
、then
、elif
、else
、fi
這幾條命令實(shí)現(xiàn)分支控制。這種流程控制語(yǔ)句本質(zhì)上也是由若干條Shell命令組成的,例如先前講過(guò)的
if [ -f ~/.bashrc ]; then . ~/.bashrcfi
其實(shí)是三條命令,if [ -f ~/.bashrc ]
是第一條,then . ~/.bashrc
是第二條,fi
是第三條。如果兩條命令寫(xiě)在同一行則需要用;號(hào)隔開(kāi),一行只寫(xiě)一條命令就不需要寫(xiě);號(hào)了,另外,then
后面有換行,但這條命令沒(méi)寫(xiě)完,Shell會(huì)自動(dòng)續(xù)行,把下一行接在then
后面當(dāng)作一條命令處理。和[
命令一樣,要注意命令和各參數(shù)之間必須用空格隔開(kāi)。if
命令的參數(shù)組成一條子命令,如果該子命令的Exit Status為0(表示真),則執(zhí)行then
后面的子命令,如果Exit Status非0(表示假),則執(zhí)行elif
、else
或者fi
后面的子命令。if
后面的子命令通常是測(cè)試命令,但也可以是其它命令。Shell腳本沒(méi)有{}括號(hào),所以用fi
表示if
語(yǔ)句塊的結(jié)束。見(jiàn)下例:
#! /bin/shif [ -f /bin/bash ]then echo "/bin/bash is a file"else echo "/bin/bash is NOT a file"fiif :; then echo "always true"; fi
:
是一個(gè)特殊的命令,稱(chēng)為空命令,該命令不做任何事,但Exit Status總是真。此外,也可以執(zhí)行/bin/true
或/bin/false
得到真或假的Exit Status。再看一個(gè)例子:
#! /bin/shecho "Is it morning? Please answer yes or no."read YES_OR_NOif [ "$YES_OR_NO" = "yes" ]; then echo "Good morning!"elif [ "$YES_OR_NO" = "no" ]; then echo "Good afternoon!"else echo "Sorry, $YES_OR_NO not recognized. Enter yes or no." exit 1fiexit 0
上例中的read
命令的作用是等待用戶輸入一行字符串,將該字符串存到一個(gè)Shell變量中。
此外,Shell還提供了&&和||語(yǔ)法,和C語(yǔ)言類(lèi)似,具有Short-circuit特性,很多Shell腳本喜歡寫(xiě)成這樣:
test "$(whoami)" != 'root' && (echo you are using a non-privileged account; exit 1)
&&相當(dāng)于“if...then...”,而||相當(dāng)于“if not...then...”。&&和||用于連接兩個(gè)命令,而上面講的-a
和-o
僅用于在測(cè)試表達(dá)式中連接兩個(gè)測(cè)試條件,要注意它們的區(qū)別,例如,
test "$VAR" -gt 1 -a "$VAR" -lt 3
和以下寫(xiě)法是等價(jià)的
test "$VAR" -gt 1 && test "$VAR" -lt 3
case
命令可類(lèi)比C語(yǔ)言的switch
/case
語(yǔ)句,esac
表示case
語(yǔ)句塊的結(jié)束。C語(yǔ)言的case
只能匹配整型或字符型常量表達(dá)式,而Shell腳本的case
可以匹配字符串和Wildcard,每個(gè)匹配分支可以有若干條命令,末尾必須以;;結(jié)束,執(zhí)行時(shí)找到第一個(gè)匹配的分支并執(zhí)行相應(yīng)的命令,然后直接跳到esac
之后,不需要像C語(yǔ)言一樣用break
跳出。
#! /bin/shecho "Is it morning? Please answer yes or no."read YES_OR_NOcase "$YES_OR_NO" inyes|y|Yes|YES) echo "Good Morning!";;[nN]*) echo "Good Afternoon!";;*) echo "Sorry, $YES_OR_NO not recognized. Enter yes or no." exit 1;;esacexit 0
使用case
語(yǔ)句的例子可以在系統(tǒng)服務(wù)的腳本目錄/etc/init.d
中找到。這個(gè)目錄下的腳本大多具有這種形式(以/etc/apache2
為例):
case $1 in start) ... ;; stop) ... ;; reload | force-reload) ... ;; restart) ... *) log_success_msg "Usage: /etc/init.d/apache2 {start|stop|restart|reload|force-reload|start-htcacheclean|stop-htcacheclean}" exit 1 ;;esac
啟動(dòng)apache2
服務(wù)的命令是
$ sudo /etc/init.d/apache2 start
$1
是一個(gè)特殊變量,在執(zhí)行腳本時(shí)自動(dòng)取值為第一個(gè)命令行參數(shù),也就是start
,所以進(jìn)入start)
分支執(zhí)行相關(guān)的命令。同理,命令行參數(shù)指定為stop
、reload
或restart
可以進(jìn)入其它分支執(zhí)行停止服務(wù)、重新加載配置文件或重新啟動(dòng)服務(wù)的相關(guān)命令。
Shell腳本的for
循環(huán)結(jié)構(gòu)和C語(yǔ)言很不一樣,它類(lèi)似于某些編程語(yǔ)言的foreach
循環(huán)。例如:
#! /bin/shfor FRUIT in apple banana pear; do echo "I like $FRUIT"done
FRUIT
是一個(gè)循環(huán)變量,第一次循環(huán)$FRUIT
的取值是apple
,第二次取值是banana
,第三次取值是pear
。再比如,要將當(dāng)前目錄下的chap0
、chap1
、chap2
等文件名改為chap0~
、chap1~
、chap2~
等(按慣例,末尾有~字符的文件名表示臨時(shí)文件),這個(gè)命令可以這樣寫(xiě):
$ for FILENAME in chap?; do mv $FILENAME $FILENAME~; done
也可以這樣寫(xiě):
$ for FILENAME in `ls chap?`; do mv $FILENAME $FILENAME~; done
while
的用法和C語(yǔ)言類(lèi)似。比如一個(gè)驗(yàn)證密碼的腳本:
#! /bin/shecho "Enter password:"read TRYwhile [ "$TRY" != "secret" ]; do echo "Sorry, try again" read TRYdone
下面的例子通過(guò)算術(shù)運(yùn)算控制循環(huán)的次數(shù):
#! /bin/shCOUNTER=1while [ "$COUNTER" -lt 10 ]; do echo "Here we go again" COUNTER=$(($COUNTER+1))done
Shell還有until循環(huán),類(lèi)似C語(yǔ)言的do...while循環(huán)。本章從略。
有很多特殊變量是被Shell自動(dòng)賦值的,我們已經(jīng)遇到了$?
和$1
,現(xiàn)在總結(jié)一下:
位置參數(shù)可以用shift
命令左移。比如shift 3
表示原來(lái)的$4
現(xiàn)在變成$1
,原來(lái)的$5
現(xiàn)在變成$2
等等,原來(lái)的$1
、$2
、$3
丟棄,$0
不移動(dòng)。不帶參數(shù)的shift
命令相當(dāng)于shift 1
。例如:
#! /bin/shecho "The program $0 is now running"echo "The first parameter is $1"echo "The second parameter is $2"echo "The parameter list is $@"shiftecho "The first parameter is $1"echo "The second parameter is $2"echo "The parameter list is $@"
和C語(yǔ)言類(lèi)似,Shell中也有函數(shù)的概念,但是函數(shù)定義中沒(méi)有返回值也沒(méi)有參數(shù)列表。例如:
#! /bin/shfoo(){ echo "Function foo is called";}echo "-=start=-"fooecho "-=end=-"
注意函數(shù)體的左花括號(hào){和后面的命令之間必須有空格或換行,如果將最后一條命令和右花括號(hào)}
寫(xiě)在同一行,命令末尾必須有;號(hào)。
在定義foo()
函數(shù)時(shí)并不執(zhí)行函數(shù)體中的命令,就像定義變量一樣,只是給foo
這個(gè)名字一個(gè)定義,到后面調(diào)用foo
函數(shù)的時(shí)候(注意Shell中的函數(shù)調(diào)用不寫(xiě)括號(hào))才執(zhí)行函數(shù)體中的命令。Shell腳本中的函數(shù)必須先定義后調(diào)用,一般把函數(shù)定義都寫(xiě)在腳本的前面,把函數(shù)調(diào)用和其它命令寫(xiě)在腳本的最后(類(lèi)似C語(yǔ)言中的main
函數(shù),這才是整個(gè)腳本實(shí)際開(kāi)始執(zhí)行命令的地方)。
Shell函數(shù)沒(méi)有參數(shù)列表并不表示不能傳參數(shù),事實(shí)上,函數(shù)就像是迷你腳本,調(diào)用函數(shù)時(shí)可以傳任意個(gè)參數(shù),在函數(shù)內(nèi)同樣是用$0
、$1
、$2
等變量來(lái)提取參數(shù),函數(shù)中的位置參數(shù)相當(dāng)于函數(shù)的局部變量,改變這些變量并不會(huì)影響函數(shù)外面的$0
、$1
、$2
等變量。函數(shù)中可以用return
命令返回,如果return
后面跟一個(gè)數(shù)字則表示函數(shù)的Exit Status。
下面這個(gè)腳本可以一次創(chuàng)建多個(gè)目錄,各目錄名通過(guò)命令行參數(shù)傳入,腳本逐個(gè)測(cè)試各目錄是否存在,如果目錄不存在,首先打印信息然后試著創(chuàng)建該目錄。
#! /bin/shis_directory(){ DIR_NAME=$1 if [ ! -d $DIR_NAME ]; then return 1 else return 0 fi}for DIR in "$@"; do if is_directory "$DIR" then : else echo "$DIR doesn't exist. Creating it now..." mkdir $DIR > /dev/null 2>&1 if [ $? -ne 0 ]; then echo "Cannot create directory $DIR" exit 1 fi fidone
注意is_directory()
返回0表示真返回1表示假。
聯(lián)系客服