一澎怒、調(diào)試腳本
調(diào)試功能是每一種編程語(yǔ)言都應(yīng)該實(shí)現(xiàn)的重要特性之一褒搔,當(dāng)出現(xiàn)一些始料未及的情況時(shí),用它來(lái)生成腳本運(yùn)行信息丹拯。調(diào)試信息可以幫你弄清楚是什么原因使得程序發(fā)生崩潰或行為異常站超。每位系統(tǒng)程序員都應(yīng)該了解Bash提供的調(diào)試選項(xiàng)。
shell腳本調(diào)試不需要什么特殊的工具乖酬。bash自帶了一些調(diào)試選項(xiàng)死相。具體選項(xiàng)包含:
-x : 在執(zhí)行時(shí)顯示參數(shù)和命令;
+x:禁止調(diào)試
-v:當(dāng)命令行進(jìn)行讀取時(shí)顯示輸入咬像;
+v:禁止打印輸入算撮。
在shell腳本啟動(dòng)時(shí)或者在腳本內(nèi)都可以添加這些調(diào)試選項(xiàng)生宛。測(cè)試腳本debug.sh,代碼如下所示肮柜。
#!/bin/bash
for i in {1..6};
do
echo $i
done
echo "Script executed"
直接運(yùn)行腳本: ./debug.sh陷舅。 結(jié)果如圖:在腳本內(nèi)添加調(diào)試選項(xiàng)芒澜,使用set 命令仰剿。例如:
要開(kāi)啟-x選項(xiàng),則在腳本內(nèi)容中添加命令:set -x 痴晦,對(duì)應(yīng)的set +x 是關(guān)閉調(diào)試南吮。
#!/bin/bash
for i in {1..6};
do
set -x
echo $i
done
echo "Script executed"
現(xiàn)在要看debug.sh腳本執(zhí)行的調(diào)試信息,就不需要使用bash -x ./debug.sh執(zhí)行了誊酌。直接./debug.sh 就可以看到調(diào)試信息部凑。結(jié)果:還有一種更便捷的方法,就是在腳本開(kāi)頭添加-xv選項(xiàng):
#!/bin/bash -xv
for i in {1..6};
do
echo $i
done
echo "Script executed”
同樣現(xiàn)在執(zhí)行./debug.sh碧浊,也可打印出調(diào)試信息涂邀。前面介紹的調(diào)試手段是Bash內(nèi)建的。它們通常以固定的格式生成調(diào)試信息辉词。但是在很多情況下必孤,我們需要以自定義格式顯示調(diào)試信息猾骡。這可以通過(guò)傳遞 _DEBUG環(huán)境變量來(lái)建立這類(lèi)調(diào)試風(fēng)格瑞躺。 請(qǐng)看下面的代碼script.sh:
#!/bin/bash
function DEBUG()
{
[ "$_DEBUG" == "on" ] && $@ || :
}
for i in {1..10}
do
DEBUG echo $i
done
可以將調(diào)試功能置為"on"來(lái)運(yùn)行上面的腳本:
_DEBUG=on ./script.sh
如果不開(kāi)啟調(diào)試開(kāi)關(guān),就直接執(zhí)行./script兴想。
二幢哨、函數(shù)和參數(shù)
inux shell 可以用戶(hù)定義函數(shù),然后在shell腳本中可以隨便調(diào)用嫂便。
shell中函數(shù)的定義格式如下:
[ function ] funname [()]
{
action;
[return int;]
}
說(shuō)明:
1捞镰、可以帶function fun() 定義,也可以直接fun() 定義,不帶任何參數(shù)毙替。
2岸售、參數(shù)返回,可以顯示加:return 返回厂画,如果不加凸丸,將以最后一條命令運(yùn)行結(jié)果,作為返回值袱院。 return后跟數(shù)值n(0-255)
下面的例子hanshu.sh定義了一個(gè)函數(shù)并進(jìn)行調(diào)用:
只需要使用函數(shù)名就可以調(diào)用某個(gè)函數(shù): $ fname ; #執(zhí)行函數(shù)
#!/bin/bash
function fname()
{
echo "這個(gè)我的第一個(gè)函數(shù)J郝2t稼。?
}
echo "---函數(shù)開(kāi)始執(zhí)行---"
fname
echo "---函數(shù)執(zhí)行完畢---"
結(jié)果如下圖:參數(shù)可以傳遞給函數(shù)腻惠,并由腳本進(jìn)行訪問(wèn):
fname arg1 arg2 ; #傳遞參數(shù)
以下是函數(shù)fname的定義环肘。在函數(shù)fname中,包含了各種訪問(wèn)函數(shù)參數(shù)的方法集灌。
#!/bin/bash
fname()
{
echo $1, $2; #訪問(wèn)參數(shù)1和參數(shù)2echo "$@";#以列表的方式一次性打印所有參數(shù)
echo "$*"; #類(lèi)似于$@悔雹,但是參數(shù)被作為單個(gè)實(shí)體 return 0; #返回值
return 0;
}
fname 1 2 3
結(jié)果如下:? $1是第一個(gè)參數(shù)。
? $2是第二個(gè)參數(shù)欣喧。
? $n是第n個(gè)參數(shù)荠商。
? "$@" 被擴(kuò)展成 "$1" "$2" "$3"等。
? "$*" 被擴(kuò)展成 "$1c$2c$3"续誉,其中c是IFS的第一個(gè)字符莱没。
? "$@" 要比"$*"用得多。由于 "$*"將所有的參數(shù)當(dāng)做單個(gè)字符串酷鸦,因此它很少被使用饰躲。
三、將命令序列的輸出讀入變量
shell腳本最棒的特性之一就是可以輕松地將多個(gè)命令或工具組合起來(lái)生成輸出臼隔。一個(gè)命令的輸出可以作為另一個(gè)命令的輸入嘹裂,而這個(gè)命令的輸出又會(huì)傳遞至另一個(gè)命令,依次類(lèi)推摔握。
這種命令組合的輸出可以被存儲(chǔ)在一個(gè)變量中寄狼。這則攻略將演示如何組合多個(gè)命令以及如何讀取其輸出。
(1) 先從組合兩個(gè)命令開(kāi)始:
ls | cat -n > out.txt
ls的輸出(當(dāng)前目錄內(nèi)容的列表)被傳給cat -n氨淌,后者將通過(guò)stdin所接收到輸入內(nèi)容加上行號(hào)泊愧,
然后將輸出重定向到文件out.txt。
(2) 我們可以用下面的方法讀取由管道相連的命令序列的輸出:這種方法被稱(chēng)為子shell盛正。
例如:cmd_output=$(ls | cat -n)
echo $cmd_output
另一種被稱(chēng)為反引用(有些人們也稱(chēng)它為反標(biāo)記)的方法也可以用于存儲(chǔ)命令輸出删咱。
例如:cmd_output=`ls | cat -n`
echo $cmd_output
有很多種方法可以給命令分組。來(lái)看看其中的幾種:
1豪筝、利用子shell生成一個(gè)獨(dú)立的進(jìn)程子shell本身就是獨(dú)立的進(jìn)程痰滋。可以使用()操作符來(lái)定義一個(gè)子shell
pwd;
(cd /bin; ls);
pwd;
當(dāng)命令在子shell中執(zhí)行時(shí)续崖,不會(huì)對(duì)當(dāng)前shell有任何影響;所有的改變僅限于子shell內(nèi)敲街。例如:當(dāng)用cd命令改變子shell的當(dāng)前目錄時(shí),這種變化不會(huì)反映到主shell環(huán)境中严望。
2多艇、通過(guò)引用子shell的方式保留空格和換行符
假設(shè)我們使用子shell或反引用的方法將命令的輸出讀入一個(gè)變量中,可以將它放入雙引號(hào)中著蟹,以保留空格和換行符(\n)墩蔓。例如:
out=$(cat text.txt)
echo $out
out="$(cat tex.txt)"
echo$out
四梢莽、Read函數(shù)
read是一個(gè)重要的Bash命令,它用于從鍵盤(pán)或標(biāo)準(zhǔn)輸入中讀取文本奸披。
我們可以使用read以交互的形式讀取來(lái)自用戶(hù)的輸入昏名,不過(guò)read能做的可遠(yuǎn)不止這些。任何編程語(yǔ)言的輸入庫(kù)大多都是從鍵盤(pán)讀取輸入;但只有當(dāng)回車(chē)鍵按下的時(shí)候阵面,才標(biāo)志著輸入完畢轻局。
在有些重要情形下是沒(méi)法按回車(chē)鍵的,輸入結(jié)束與否是基于字符數(shù)或某個(gè)特定字符來(lái)決定的样刷。
例如仑扑,在一個(gè)游戲中,當(dāng) 按下 + 鍵時(shí)置鼻,小球就會(huì)向上移動(dòng)镇饮。那么若每次都要按下 + 鍵,然后再按回車(chē)鍵來(lái)確認(rèn)已經(jīng)按過(guò) + 鍵箕母,這就顯然太低效了储藐。
read命令提供了一種不需要按回車(chē)鍵就能夠搞定這個(gè)任務(wù)的方法。
你可以借助read命令的各種選項(xiàng)來(lái)實(shí)現(xiàn)不同的效果嘶是。方法如下所示:
(1) 下面的語(yǔ)句從輸入中讀取n個(gè)字符并存入變量:
read -n 2 var
echo $var
(2) 用無(wú)回顯的方式讀取密碼:
read -s var
(3) 顯示提示信息:
read -p "Enter input:" var
(4) 在特定時(shí)限內(nèi)讀取輸入:
read -t 2 var #在2秒內(nèi)將鍵入的字符串,且鍵入回車(chē)后钙勃,讀入變量var
(5) 用特定的定界符作為輸入行的結(jié)束:
read -d ":" var
hello:#var 被設(shè)置為 hello
(6)read命令是一個(gè)一個(gè)詞組地接收輸入的參數(shù),每個(gè)詞組需要使用空格進(jìn)行分隔聂喇;如果輸入的詞組個(gè)數(shù)大于需要的參數(shù)個(gè)數(shù)辖源,則多出的詞組將被作為整體為最后一個(gè)參數(shù)接收。
read firstStr secondStr
echo "第一個(gè)參數(shù):$firstStr; 第二個(gè)參數(shù):$secondStr"
執(zhí)行測(cè)試:
./test.sh
一 二 三 四
第一個(gè)參數(shù):一; 第二個(gè)參數(shù):二 三 四
五希太、運(yùn)行命令直至執(zhí)行成功
在日常工作中使用shell時(shí)克饶,有時(shí)候命令只有滿足某些條件或是某種外部事件(例如文件可以被下載)操作才能夠成功執(zhí)行。這種情況下跛十,你可能希望重復(fù)執(zhí)行命令彤路,直到成功為止秕硝。
重復(fù)執(zhí)行命令函數(shù):
repeat() {while :;do $@ && return; done}
修改間隔時(shí)間后再次執(zhí)行芥映,默認(rèn)會(huì)不斷執(zhí)行:
repeat() {while :; do sleep 30; $@ && return ; done}
舉例:
#!/bin/bash
repeat()
{
while :;
do
sleep 30 ;
$@ && return ;
done
}
echo "開(kāi)始計(jì)時(shí)"
echo `date`
repeat pwd
echo `date`
運(yùn)行腳本,等待30S后远豺,在執(zhí)行pwd奈偏。
六、字段分隔符和迭代器
內(nèi)部字段分隔符(Internal Field Separator躯护,IFS)是shell腳本編程中的一個(gè)重要概念惊来。在處理文本數(shù)據(jù)時(shí),它的用途可不小棺滞。我們將會(huì)討論把單個(gè)數(shù)據(jù)流劃分成不同數(shù)據(jù)元素的定界符 (delimiter)裁蚁。內(nèi)部字段分隔符是用于特定用途的定界符矢渊。IFS是存儲(chǔ)定界符的環(huán)境變量。它是當(dāng)前shell環(huán)境使用的默認(rèn)定界字符串枉证。
1矮男、查看IFS的值
echo “$IFS"
直接輸出IFS是看不到值的,轉(zhuǎn)化為二進(jìn)制就可以看到了:
echo "$IFS"|od -b
輸出結(jié)果:
0000000 040 011 012 012
0000004
"040"是空格室谚,"011"是Tab毡鉴,"012"是換行符"\n" 。最后一個(gè) 012 是因?yàn)?echo 默認(rèn)是會(huì)換行的秒赤。
2猪瞬、IFS的默認(rèn)值為空白字符(換行符、制表符或者空格)入篮。
當(dāng)IFS被設(shè)置為逗號(hào)時(shí)陈瘦,shell將逗號(hào)視為一個(gè)定界符,因此變量$item在每次迭代中讀取由逗號(hào)分隔的子串作為變量值潮售。 如果沒(méi)有把IFS設(shè)置成逗號(hào)甘晤,那么下面的腳本會(huì)將全部數(shù)據(jù)作為單個(gè)字符串打印出來(lái)。實(shí)例:
#!/bin/bash
data="name,sex,rollno,location"
old_IFS=$IFS
IFS=","
for item in $data;
do
echo Item: $item
done
IFS=$old_IFS
輸出結(jié)果:
Item: name
Item: sex
Item: rollno
Item: location
七饲做、比較與測(cè)試
程序中的流程控制是由比較語(yǔ)句和測(cè)試語(yǔ)句處理的线婚。Bash同樣具備多種與Unix系統(tǒng)級(jí)特性相兼容的執(zhí)行測(cè)試的方法。我們可以用if盆均、if else以及邏輯運(yùn)算符進(jìn)行測(cè)試塞弊,用比較運(yùn)算符來(lái)比較數(shù)據(jù)項(xiàng)。除此之外泪姨,還有一個(gè)test命令也可以用于測(cè)試游沿。
來(lái)看看用于比較和測(cè)試的各種方法:
1、if條件
if condition;
then
commands; fi
2肮砾、else if和else
if condition;
then
commands;
else if condition; then
commands;
else
commands; fi
if和else語(yǔ)句可以進(jìn)行嵌套诀黍。if的條件判斷部分可能會(huì)變得很長(zhǎng),但可以用邏輯運(yùn)算符將它變得簡(jiǎn)潔一些:
? [ condition ] && action; # 如果condition為真仗处,則執(zhí)行action;
? [ condition ] || action; # 如果condition為假眯勾,則執(zhí)行action。
&&是邏輯與運(yùn)算符婆誓,||是邏輯或運(yùn)算符吃环。編寫(xiě)B(tài)ash腳本時(shí),這是一個(gè)很有用的技巧洋幻。
1郁轻、算數(shù)比較
條件通常被放置在封閉的中括號(hào)內(nèi)。一定要注意在[或]與操作數(shù)之間有一個(gè)空格。如果
忘記了這個(gè)空格好唯,腳本就會(huì)報(bào)錯(cuò)竭沫。
對(duì)變量或值進(jìn)行算術(shù)條件判斷:
[ $var -eq 0 ] #當(dāng)$var 等于0時(shí),返回真
[ $var -ne 0 ] #當(dāng)$var 為非0時(shí)骑篙,返回真
其他重要的操作符如下所示:
? -gt:大于输吏。
? -lt:小于。
? -ge:大于或等于替蛉。
? -le:小于或等于贯溅。
可以按照下面的方法結(jié)合多個(gè)條件進(jìn)行測(cè)試:
[ $var1 -ne 0 -a $var2 -gt 2 ] #使用邏輯與-a
[ $var1 -ne 0 -o var2 -gt 2 ] #邏輯或 -o
腳本實(shí)例:
#!/bin/bash
var=133
if [ $var -ne 0 ]
then
echo True
else
echo Flase
fi
很明顯,結(jié)果返回True.
2躲查、字符串比較
使用字符串比較時(shí)它浅,最好用雙中括號(hào),因?yàn)橛袝r(shí)候采用單個(gè)中括號(hào)會(huì)產(chǎn)生錯(cuò)誤镣煮,所以最好避開(kāi)它們姐霍。
可以用下面的方法檢查兩個(gè)字符串,看看它們是否相同典唇。
??注:注意在 = 前后各有一個(gè)空格镊折。如果忘記加空格,那就不是比較關(guān)系了介衔,而變成了賦值語(yǔ)句恨胚。
[[ $str1 = $str2 ]]:當(dāng)str1等于str2時(shí),返回真炎咖。也就是說(shuō)赃泡,str1和str2包含的文本是一模一樣的。
[[ $str1 == $str2 ]]:這是檢查字符串是否相等的另一種寫(xiě)法乘盼。也可以檢查兩個(gè)字符串是否不同升熊。
[[ $str1 != $str2 ]]:如果str1和str2不相同,則返回真绸栅。
[[ $str1 > $str2 ]]:如果str1的字母序比str2大级野,則返回真。
[[ $str1 < $str2 ]]:如果str1的字母序比str2小粹胯,則返回真蓖柔。
[[ -z $str1 ]]:如果str1包含的是空字符串,則返回真矛双。
[[ -n $str1 ]]:如果str1包含的是非空字符串渊抽,則返回真。
腳本實(shí)例:
#!/bin/bash
str1=abcd
str2=dfgh
if [[ $str1 = $str2 ]]
then
echo True
else
echo Flase
fi
很明顯议忽,結(jié)果返回Flase.
使用邏輯運(yùn)算符 && 和 || 能夠很容易地將多個(gè)條件組合起來(lái)。 見(jiàn)實(shí)例:
#!/bin/bash
str1=abcd
str2=dfgh
if [[ $str1 = $str2 ]] && [[ $str1 != $str2 ]]
then
echo True
else
echo Flase
fi
test命令可以用來(lái)執(zhí)行條件檢測(cè)十减。用test可以避免使用過(guò)多的括號(hào)栈幸。見(jiàn)實(shí)例:
if [ $var -eq 0 ]; then echo "True"; fi
也可以寫(xiě)成:
if test $var -eq 0 ; then echo "True"; fi
3愤估、文件系統(tǒng)相關(guān)測(cè)試
我們可以使用不同的條件標(biāo)志測(cè)試不同的文件系統(tǒng)相關(guān)的屬性。
[ -f $file_var ]:如果給定的變量包含正常的文件路徑或文件名速址,則返回真玩焰。
[ -x $var ]:如果給定的變量包含的文件可執(zhí)行,則返回真芍锚。
[ -d $var ]:如果給定的變量包含的是目錄昔园,則返回真。
[ -e $var ]:如果給定的變量包含的文件存在并炮,則返回真默刚。
[ -c $var ]:如果給定的變量包含的是一個(gè)字符設(shè)備文件的路徑,則返回真逃魄。
[ -b $var ]:如果給定的變量包含的是一個(gè)塊設(shè)備文件的路徑荤西,則返回真。
[ -w $var ]:如果給定的變量包含的文件可寫(xiě)伍俘,則返回真邪锌。
[ -r $var ]:如果給定的變量包含的文件可讀,則返回真癌瘾。
[ -L $var ]:如果給定的變量包含的是一個(gè)符號(hào)鏈接觅丰,則返回真。
腳本實(shí)例:
#!/bin/bash
fpath="/etc/passwd"
if [ -e $fpath ]; then
echo File exists;
else
echo Does not exist;
fi
以上妨退,未完待續(xù)~~