八掘譬、Shell編程

搭配《linux就該這樣學(xué)》第四章內(nèi)容

1.shell歷史

Shell的作用是解釋執(zhí)行用戶的命令丽蝎,用戶輸入一條命令,Shell就解釋執(zhí)行一條雾鬼,這種方式稱為交互式(Interactive)萌朱,Shell還有一種執(zhí)行命令的方式稱為批處(Batch),用戶事先寫一個(gè)Shell腳本(Script)策菜,其中有很多條命令晶疼,讓Shell一次把這些命令執(zhí)行完,而不必一條一條地敲命令又憨。Shell腳本和編程語言很相似翠霍,也有變量和流程控制語句,但Shell腳本是解釋執(zhí)行的蠢莺,不需要編譯寒匙,Shell程序從腳本中一行一行讀取并執(zhí)行這些命令,相當(dāng)于一個(gè)用戶把腳本中的命令一行行敲到Shell提示符下執(zhí)行浪秘。

由于歷史原因蒋情,UNIX系統(tǒng)上有很多種Shell:

  1. sh(Bourne Shell):由Steve Bourne開發(fā),各種UNIX系統(tǒng)都配有sh耸携。
  2. csh(C Shell):由Bill Joy開發(fā)棵癣,隨BSD UNIX發(fā)布,它的流程控制語句很像C語言夺衍,支持很多Bourne Shell所不支持的功能:作業(yè)控制狈谊,命令歷史,命令行編輯沟沙。
  3. ksh(Korn Shell):由David Korn開發(fā)河劝,向后兼容sh的功能,并且添加了csh引入的新功能矛紫,是目前很多UNIX系統(tǒng)標(biāo)準(zhǔn)配置的Shell赎瞎,在這些系統(tǒng)上/bin/sh往往是指向/bin/ksh的符號(hào)鏈接。
  4. tcsh(TENEX C Shell):是csh的增強(qiáng)版本颊咬,引入了命令補(bǔ)全等功能务甥,在FreeBSD、Mac OS X等系統(tǒng)上替代了csh喳篇。
  5. bash(Bourne Again Shell):由GNU開發(fā)的Shell敞临,主要目標(biāo)是與POSIX標(biāo)準(zhǔn)保持一致,同時(shí)兼顧對(duì)sh的兼容麸澜,bash從csh和ksh借鑒了很多功能挺尿,是各種Linux發(fā)行版標(biāo)準(zhǔn)配置的Shell,在Linux系統(tǒng)上/bin/sh往往是指向/bin/bash的符號(hào)鏈接炊邦。雖然如此编矾,bash和sh還是有很多不同的,一方面铣耘,bash擴(kuò)展了一些命令和參數(shù)洽沟,另一方面,bash并不完全和sh兼容蜗细,有些行為并不一致裆操,所以bash需要模擬sh的行為:當(dāng)我們通過sh這個(gè)程序名啟動(dòng)bash時(shí),bash可以假裝自己是sh炉媒,不認(rèn)擴(kuò)展的命令踪区,并且行為與sh保持一致。
vim /etc/passwd
其中最后一列顯示了用戶對(duì)應(yīng)的shell類型
root:x:0:0:root:/root:/bin/bash
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
syslog:x:101:103::/home/syslog:/bin/false
myusr:x:1000:1000:itcast,,,:/home/itcast:/bin/bash
ftp:x:115:125:ftp daemon,,,:/srv/ftp:/bin/false

2.執(zhí)行腳本

編寫一個(gè)簡(jiǎn)單的腳本test.sh:

#! /bin/sh
cd ..
ls

Shell腳本中用#表示注釋吊骤,相當(dāng)于C語言的//注釋缎岗。但如果#位于第一行開頭,并且是#!(稱為Shebang)則例外白粉,它表示該腳本使用后面指定的解釋器/bin/sh解釋執(zhí)行传泊。如果把這個(gè)腳本文件加上可執(zhí)行權(quán)限然后執(zhí)行:

chmod a+x test.sh
./test.sh

Shell會(huì)fork一個(gè)子進(jìn)程并調(diào)用exec執(zhí)行./test.sh這個(gè)程序鼠渺,exec系統(tǒng)調(diào)用應(yīng)該把子進(jìn)程的代碼段替換成./test.sh程序的代碼段,并從它的_start開始執(zhí)行眷细。然而test.sh是個(gè)文本文件拦盹,根本沒有代碼段和_start函數(shù),怎么辦呢溪椎?其實(shí)exec還有另外一種機(jī)制普舆,如果要執(zhí)行的是一個(gè)文本文件,并且第一行用Shebang指定了解釋器校读,則用解釋器程序的代碼段替換當(dāng)前進(jìn)程沼侣,并且從解釋器的_start開始執(zhí)行,而這個(gè)文本文件被當(dāng)作命令行參數(shù)傳給解釋器歉秫。因此蛾洛,執(zhí)行上述腳本相當(dāng)于執(zhí)行程序

$ /bin/sh ./test.sh

以這種方式執(zhí)行不需要test.sh文件具有可執(zhí)行權(quán)限。
如果將命令行下輸入的命令用()括號(hào)括起來端考,那么也會(huì)fork出一個(gè)子Shell執(zhí)行小括號(hào)中的命令雅潭,一行中可以輸入由分號(hào);隔開的多個(gè)命令,比如:

$ (cd ..;ls -l)

3.基本語法

3.1變量

按照慣例却特,Shell變量由全大寫字母加下劃線組成扶供,有兩種類型的Shell變量:

環(huán)境變量
環(huán)境變量可以從父進(jìn)程傳給子進(jìn)程,因此Shell進(jìn)程的環(huán)境變量可以從當(dāng)前Shell進(jìn)程傳給fork出來的子進(jìn)程裂明。用printenv命令可以顯示當(dāng)前Shell進(jìn)程的環(huán)境變量椿浓。

本地變量
只存在于當(dāng)前Shell進(jìn)程,用set命令可以顯示當(dāng)前Shell進(jìn)程中定義的所有變量(包括本地變量和環(huán)境變量)和函數(shù)闽晦。

環(huán)境變量是任何進(jìn)程都有的概念扳碍,而本地變量是Shell特有的概念。在Shell中仙蛉,環(huán)境變量和本地變量的定義和用法相似笋敞。在Shell中定義或賦值一個(gè)變量:
$ VARNAME=value
注意等號(hào)兩邊都不能有空格,否則會(huì)被Shell解釋成命令和命令行參數(shù)荠瘪。

一個(gè)變量定義后僅存在于當(dāng)前Shell進(jìn)程夯巷,它是本地變量,用export命令可以把本地變量導(dǎo)出為環(huán)境變量哀墓,定義和導(dǎo)出環(huán)境變量通吵貌停可以一步完成:
$ export VARNAME=value

也可以分兩步完成:

$ VARNAME=value
$ export VARNAME

用unset命令可以刪除已定義的環(huán)境變量或本地變量。
itcast$ unset VARNAME

注意篮绰,在定義變量時(shí)不用后雷,取變量值時(shí)要用。和C語言不同的是,Shell變量不需要明確定義類型臀突,事實(shí)上Shell變量的值都是字符串勉抓,比如我們定義VAR=45,其實(shí)VAR的值是字符串45而非整數(shù)候学。Shell變量不需要先定義后使用琳状,如果對(duì)一個(gè)沒有定義的變量取值,則值為空字符串盒齿。

3.2文件名代換(Globbing):* ? []

這些用于匹配的字符稱為通配符(Wildcard),具體如下:

通配符
* 匹配0個(gè)或多個(gè)任意字符
? 匹配一個(gè)任意字符
[若干字符] 匹配方括號(hào)中任意一個(gè)字符的一次出現(xiàn)
$ ls /dev/ttyS*
$ ls ch0?.doc
$ ls ch0[0-2].doc
$ ls ch[012] [0-9].doc

注意困食,Globbing所匹配的文件名是由Shell展開的边翁,也就是說在參數(shù)還沒傳給程序之前已經(jīng)展開了,比如上述ls ch0[012].doc命令硕盹,如果當(dāng)前目錄下有ch00.doc和ch02.doc符匾,則傳給ls命令的參數(shù)實(shí)際上是這兩個(gè)文件名,而不是一個(gè)匹配字符串瘩例。

3.3命令代換:‘ 或$()

由’‘’反引號(hào)括起來的也是一條命令啊胶,Shell先執(zhí)行該命令,然后將輸出結(jié)果立刻代換到當(dāng)前命令行中垛贤。例如定義一個(gè)變量存放date命令的輸出:

DATE=`date`
echo $DATE
DATE=$(date)

PS:注意區(qū)分' '和" "和` `的卻別

3.4算術(shù)代換:$(())

用于算術(shù)計(jì)算焰坪,(())中的Shell變量取值將轉(zhuǎn)換成整數(shù),同樣含義的[]等價(jià)例如:

$ VAR=45
$ echo $(($VAR+3))
$(())中只能用+-*/和()運(yùn)算符聘惦,并且只能做整數(shù)運(yùn)算某饰。

$[base#n],其中base表示進(jìn)制,n按照base進(jìn)制解釋,后面再有運(yùn)算數(shù)善绎,按十進(jìn)制解釋黔漂。
echo $[2#10+11]
echo $[8#10+11]
echo $[10#10+11]
3.5轉(zhuǎn)義字符

和C語言類似,\在Shell中被用作轉(zhuǎn)義字符禀酱,用于去除緊跟其后的單個(gè)字符的特殊意義(回車除外)炬守,換句話說,緊跟其后的字符取字面值剂跟。例如:

$ echo $SHELL
/bin/bash
$ echo \$SHELL
$SHELL
$ echo \\
\

比如創(chuàng)建一個(gè)文件名為“”的文件可以這樣:
$ touch \$\ \$
還有一個(gè)字符雖然不具有特殊含義减途,但是要用它做文件名也很麻煩,就是 - 號(hào)浩聋。如果要?jiǎng)?chuàng)建一個(gè)文件名以 - 號(hào)開頭的文件观蜗,這樣是不行的:

$ touch -hello
touch: invalid option -- h
Try `touch --help` for more information.
即使加上\轉(zhuǎn)義也還是報(bào)錯(cuò):
$ touch \-hello
touch: invalid option -- h
Try `touch --help` for more information.

因?yàn)楦鞣NUNIX命令都把-號(hào)開頭的命令行參數(shù)當(dāng)作命令的選項(xiàng),而不會(huì)當(dāng)作文件名衣洁。如果非要處理以-號(hào)開頭的文件名墓捻,可以有兩種辦法:
itcast$ touch ./-hello
或者
itcast$ touch -- -hello

3.6單引號(hào)

和C語言不一樣,Shell腳本中的單引號(hào)和雙引號(hào)一樣都是字符串的界定符(雙引號(hào)下一節(jié)介紹),而不是字符的界定符砖第。單引號(hào)用于保持引號(hào)內(nèi)所有字符的字面值撤卢,即使引號(hào)內(nèi)的\和回車也不例外,但是字符串中不能出現(xiàn)單引號(hào)梧兼。如果引號(hào)沒有配對(duì)就輸入回車放吩,Shell會(huì)給出續(xù)行提示符,要求用戶把引號(hào)配上對(duì)羽杰。例如:

$ echo '$SHELL'
$SHELL
$ echo 'ABC\(回車)
> DE'(再按一次回車結(jié)束命令)
ABC\
DE
3.7雙引號(hào)

被雙引號(hào)用括住的內(nèi)容渡紫,將被視為單一字串。它防止通配符擴(kuò)展考赛,但允許變量擴(kuò)展惕澎。這點(diǎn)與單引號(hào)的處理方式不同

$ DATE=$(date)
$ echo "$DATE"
$ echo '$DATE'

4.Shell腳本語法

命令test或[可以測(cè)試一個(gè)條件是否成立,如果測(cè)試結(jié)果為真颜骤,則該命令的Exit Status為0唧喉,如果測(cè)試結(jié)果為假,則命令的Exit Status為1(注意與C語言的邏輯表示正好相反)忍抽。例如測(cè)試兩個(gè)數(shù)的大小關(guān)系:

$ var=2
$ test $var -gt 1
$ echo $?
0

$ test $var -gt 3
$ echo $?
1

$ [ $var -gt 3 ]
$ echo $?
1

雖然看起來很奇怪八孝,但左方括號(hào)[確實(shí)是一個(gè)命令的名字,傳給命令的各參數(shù)之間應(yīng)該用空格隔開鸠项,比如干跛,$VAR、-gt祟绊、3驯鳖、]是[命令的四個(gè)參數(shù),它們之間必須用空格隔開久免。命令test或[的參數(shù)形式是相同的浅辙,只不過test命令不需要]參數(shù)。以[命令為例阎姥,常見的測(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語言類似,測(cè)試條件之間還可以做與呼巴、或泽腮、非邏輯運(yùn)算:

帶與、或衣赶、非的測(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變量事先沒有定義,則被Shell展開為空字符串,會(huì)造成測(cè)試條件的語法錯(cuò)誤(展開為[ -d Desktop -a = ‘a(chǎn)bc’ ])鲸郊,作為一種好的Shell編程習(xí)慣丰榴,應(yīng)該總是把變量取值放在雙引號(hào)之中(展開為[ -d Desktop -a “” = ‘a(chǎn)bc’ ]):

$ unset VAR
$ [ -d Desktop -a $VAR = 'abc' ]
bash: [: too many arguments
$ [ -d Desktop -a "$VAR" = 'abc' ]
$ echo $?
1
4.2if/then/elif/else/fi

和C語言類似,在Shell中用if秆撮、then四濒、elif、else职辨、fi這幾條命令實(shí)現(xiàn)分支控制盗蟆。這種流程控制語句本質(zhì)上也是由若干條Shell命令組成的,例如先前講過的

if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi

其實(shí)是三條命令舒裤,if [ -f /.bashrc ]是第一條姆涩,then . /.bashrc是第二條,fi是第三條惭每。如果兩條命令寫在同一行則需要用;號(hào)隔開,一行只寫一條命令就不需要寫;號(hào)了亏栈,另外台腥,then后面有換行,但這條命令沒寫完绒北,Shell會(huì)自動(dòng)續(xù)行黎侈,把下一行接在then后面當(dāng)作一條命令處理。和[命令一樣闷游,要注意命令和各參數(shù)之間必須用空格隔開峻汉。if命令的參數(shù)組成一條子命令,如果該子命令的Exit Status為0(表示真)脐往,則執(zhí)行then后面的子命令休吠,如果Exit Status非0(表示假),則執(zhí)行elif业簿、else或者fi后面的子命令瘤礁。if后面的子命令通常是測(cè)試命令,但也可以是其它命令梅尤。Shell腳本沒有{}括號(hào)柜思,所以用fi表示if語句塊的結(jié)束。見下例:

#! /bin/sh
if [ -f /bin/bash ]
then echo "/bin/bash is a file"
else echo "/bin/bash is NOT a file"
fi
if :; then echo "always true"; fi

:是一個(gè)特殊的命令巷燥,稱為空命令赡盘,該命令不做任何事,但Exit Status總是真缰揪。此外陨享,也可以執(zhí)行/bin/true或/bin/false得到真或假的Exit Status。再看一個(gè)例子:

#! /bin/sh
echo "Is it morning? Please answer yes or no."
read YES_OR_NO
if [ "$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 1
fi
exit 0

上例中的read命令的作用是等待用戶輸入一行字符串,將該字符串存到一個(gè)Shell變量中霉咨。

此外蛙紫,Shell還提供了&&和||語法,和C語言類似途戒,具有Short-circuit特性坑傅,很多Shell腳本喜歡寫成這樣:

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

和以下寫法是等價(jià)的

test "$VAR" -gt 1 && test "$VAR" -lt 3
4.3case/esac

case命令可類比C語言的switch/case語句,esac表示case語句塊的結(jié)束顽腾。C語言的case只能匹配整型或字符型常量表達(dá)式近零,而Shell腳本的case可以匹配字符串和通配符(wildcard),每個(gè)匹配分支可以有若干條命令抄肖,末尾必須以;;結(jié)束久信,執(zhí)行時(shí)找到第一個(gè)匹配的分支并執(zhí)行相應(yīng)的命令,然后直接跳到esac之后漓摩,不需要像C語言一樣用break跳出裙士。

#! /bin/sh
echo "Is it morning? Please answer yes or no."
read YES_OR_NO
case "$YES_OR_NO" in
yes|y|Yes|YES)   #只有右小括號(hào)!9鼙小腿椎!
echo "Good Morning!";;
[nN]*)           #只有右小括號(hào)!X惨А啃炸!
echo "Good Afternoon!";;
*)               #只有右小括號(hào)!W慷妗肮帐!
echo "Sorry, $YES_OR_NO not recognized. Enter yes or no."
exit 1;;
esac
exit 0

使用case語句的例子可以在系統(tǒng)服務(wù)的腳本目錄/etc/init.d中找到。這個(gè)目錄下的腳本大多具有這種形式(以/etc/init.d/nfs-kernel-server為例):

case "$1" in
start)
...
;;
stop)
...
;;
reload | force-reload)
...
;;
restart)
...
*)
log_success_msg "Usage: nfs-kernel-server {start|stop|status|reload|force-reload|restart}"
exit 1
;;
esac

啟動(dòng)nfs-kernel-server服務(wù)的命令是

$ sudo /etc/init.d/nfs-kernel-server 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)命令十酣。

4.4for/do/done

Shell腳本的for循環(huán)結(jié)構(gòu)和C語言很不一樣涩拙,它類似于某些編程語言的for each循環(huán)。例如:

#! /bin/sh
for 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è)命令可以這樣寫:

$ for FILENAME in chap?; do mv $FILENAME $FILENAME~; done

也可以這樣寫:

$ for FILENAME in `ls chap?`; do mv $FILENAME $FILENAME~; done
4.5 while/do/done

while的用法和C語言類似。比如一個(gè)驗(yàn)證密碼的腳本:

#! /bin/sh
echo "Enter password:"
read TRY
while [ "$TRY" != "secret" ]; do
echo "Sorry, try again"
read TRY
done

下面的例子通過算術(shù)運(yùn)算控制循環(huán)的次數(shù):

#! /bin/sh
COUNTER=1
while [ "$COUNTER" -lt 10 ]; do
echo "Here we go again"
COUNTER=$(($COUNTER+1))
done
4.6 位置參數(shù)和特殊變量

有很多特殊變量是被Shell自動(dòng)賦值的岸军,我們已經(jīng)遇到了?和1奋刽,現(xiàn)在總結(jié)一下:
常用的位置參數(shù)和特殊變量

$0 相當(dāng)于C語言main函數(shù)的argv[0]
$1、$2... 這些稱為位置參數(shù)(Positional Parameter)艰赞,相當(dāng)于C語言main函數(shù)的argv[1]佣谐、argv[2]...
$# 相當(dāng)于C語言main函數(shù)的argc - 1,注意這里的#后面不表示注釋
$@ 表示參數(shù)列表"$1" "$2" ...猖毫,例如可以用在for循環(huán)中的in后面。
$* 表示參數(shù)列表"$1" "$2" ...须喂,同上
$? 上一條命令的Exit Status
$$ 當(dāng)前進(jìn)程號(hào)

位置參數(shù)可以用shift命令左移吁断。比如shift 3表示原來的4現(xiàn)在變成1,原來的5現(xiàn)在變成2等等坞生,原來的1仔役、2、3丟棄是己,0不移動(dòng)又兵。不帶參數(shù)的shift命令相當(dāng)于shift 1。例如:

#! /bin/sh
echo "The program $0 is now running"
echo "The first parameter is $1"
echo "The second parameter is $2"
echo "The parameter list is $@"
shift
echo "The first parameter is $1"
echo "The second parameter is $2"
echo "The parameter list is $@"
4.7shell輸入輸出

echo echo顯示文本行或變量卒废,或者把字符串輸入到文件沛厨。

echo [option] string
-e 解析轉(zhuǎn)義字符
-n 不回車換行。默認(rèn)情況echo回顯的內(nèi)容后面跟一個(gè)回車換行摔认。
echo "hello\n\n"
echo -e "hello\n\n"
echo "hello"
echo -n "hello"

管道| 可以通過管道把一個(gè)命令的輸出傳遞給另一個(gè)命令做輸入逆皮。管道用豎線表示。

cat myfile | more
ls -l | grep "myfile"
df -k | awk '{print $1}' | grep -v "文件系統(tǒng)"
df -k 查看磁盤空間参袱,找到第一列电谣,去除“文件系統(tǒng)”秽梅,并輸出

文件重定向

cmd > file 把標(biāo)準(zhǔn)輸出重定向到新文件中
cmd >> file 追加
cmd > file 2>&1 標(biāo)準(zhǔn)出錯(cuò)也重定向到1所指向的file里
cmd >> file 2>&1
cmd < file1 > file2 輸入輸出都定向到文件里
cmd < &fd 把文件描述符fd作為標(biāo)準(zhǔn)輸入
cmd > &fd 把文件描述符fd作為標(biāo)準(zhǔn)輸出
cmd < &- 關(guān)閉標(biāo)準(zhǔn)輸入
4.8函數(shù)

和C語言類似,Shell中也有函數(shù)的概念剿牺,但是函數(shù)定義中沒有返回值也沒有參數(shù)列表企垦。例如:

#! /bin/sh
foo(){ echo "Function foo is called";}
echo "-=start=-"
foo
echo "-=end=-"

注意函數(shù)體的左花括號(hào)’{‘和后面的命令之間必須有空格或換行,如果將最后一條命令和右花括號(hào)’}’寫在同一行晒来,命令末尾必須有;號(hào)钞诡。

在定義foo()函數(shù)時(shí)并不執(zhí)行函數(shù)體中的命令,就像定義變量一樣潜索,只是給foo這個(gè)名字一個(gè)定義臭增,到后面調(diào)用foo函數(shù)的時(shí)候(注意Shell中的函數(shù)調(diào)用不寫括號(hào))才執(zhí)行函數(shù)體中的命令。Shell腳本中的函數(shù)必須先定義后調(diào)用竹习,一般把函數(shù)定義都寫在腳本的前面誊抛,把函數(shù)調(diào)用和其它命令寫在腳本的最后(類似C語言中的main函數(shù),這才是整個(gè)腳本實(shí)際開始執(zhí)行命令的地方)整陌。

Shell函數(shù)沒有參數(shù)列表并不表示不能傳參數(shù)拗窃,事實(shí)上,函數(shù)就像是迷你腳本泌辫,調(diào)用函數(shù)時(shí)可以傳任意個(gè)參數(shù)随夸,在函數(shù)內(nèi)同樣是用$0、$1震放、$2等變量來提取參數(shù)宾毒,函數(shù)中的位置參數(shù)相當(dāng)于函數(shù)的局部變量,改變這些變量并不會(huì)影響函數(shù)外面的$0殿遂、$1诈铛、$2等變量。函數(shù)中可以用return命令返回墨礁,如果return后面跟一個(gè)數(shù)字則表示函數(shù)的Exit Status幢竹。

下面這個(gè)腳本可以一次創(chuàng)建多個(gè)目錄,各目錄名通過命令行參數(shù)傳入恩静,腳本逐個(gè)測(cè)試各目錄是否存在焕毫,如果目錄不存在,首先打印信息然后試著創(chuàng)建該目錄驶乾。

#! /bin/sh
is_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
  fi
done
注意is_directory()返回0表示真返回1表示假邑飒。

5.Shell腳本的調(diào)試方法

Shell提供了一些用于調(diào)試腳本的選項(xiàng),如下所示:
-n
讀一遍腳本中的命令但不執(zhí)行级乐,用于檢查腳本中的語法錯(cuò)誤

-v
一邊執(zhí)行腳本幸乒,一邊將執(zhí)行過的腳本命令打印到標(biāo)準(zhǔn)錯(cuò)誤輸出

-x
提供跟蹤執(zhí)行信息,將執(zhí)行的每一條命令和結(jié)果依次打印出來
使用這些選項(xiàng)有三種方法唇牧,一是在命令行提供參數(shù)

$ sh -x ./script.sh
#! /bin/sh -x

第三種方法是在腳本中用set命令啟用或禁用參數(shù)

#! /bin/sh
if [ -z "$1" ]; then
  set -x
  echo "ERROR: Insufficient Args."
  exit 1
  set +x
fi

set -x和set +x分別表示啟用和禁用-x參數(shù)罕扎,這樣可以只對(duì)腳本中的某一段進(jìn)行跟蹤調(diào)試聚唐。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市腔召,隨后出現(xiàn)的幾起案子杆查,更是在濱河造成了極大的恐慌,老刑警劉巖臀蛛,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件亲桦,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡浊仆,警方通過查閱死者的電腦和手機(jī)客峭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來抡柿,“玉大人舔琅,你說我怎么就攤上這事≈蘖樱” “怎么了备蚓?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)囱稽。 經(jīng)常有香客問我郊尝,道長(zhǎng),這世上最難降的妖魔是什么战惊? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任流昏,我火速辦了婚禮,結(jié)果婚禮上吞获,老公的妹妹穿的比我還像新娘况凉。我一直安慰自己,他們只是感情好衫哥,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布茎刚。 她就那樣靜靜地躺著襟锐,像睡著了一般撤逢。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上粮坞,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天蚊荣,我揣著相機(jī)與錄音,去河邊找鬼莫杈。 笑死互例,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的筝闹。 我是一名探鬼主播媳叨,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼腥光,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了糊秆?” 一聲冷哼從身側(cè)響起武福,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎痘番,沒想到半個(gè)月后捉片,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡伍纫,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了莹规。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡说铃,死狀恐怖访惜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情腻扇,我是刑警寧澤债热,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站幼苛,受9級(jí)特大地震影響窒篱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜舶沿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一墙杯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧括荡,春花似錦高镐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至邑闲,卻和暖如春算行,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背苫耸。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國(guó)打工州邢, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人褪子。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓量淌,卻偏偏與公主長(zhǎng)得像骗村,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子呀枢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容