一、調試腳本
調試功能是每一種程式設計語言都應該實作的重要特性之一,當出現一些始料未及的情況時,用它來生成腳本運作資訊。調試資訊可以幫你弄清楚是什麼原因使得程式發生崩潰或行為異常。每位系統程式員都應該了解Bash提供的調試選項。
shell腳本調試不需要什麼特殊的工具。bash自帶了一些調試選項。具體選項包含:
-x : 在執行時顯示參數和指令;
+x:禁止調試
-v:當指令行進行讀取時顯示輸入;
+v:禁止列印輸入。
在shell腳本啟動時或者在腳本内都可以添加這些調試選項。測試腳本debug.sh,代碼如下所示。
#!/bin/bash
for i in {1..6};
do
echo $i
done
echo "Script executed"
直接運作腳本: ./debug.sh。 結果如圖:

在腳本啟動時添加調試選項。來調試debug.sh,可以在啟動腳本時,輸入以下指令:bash -x ./debug.sh 或者 sh -x ./debug.sh。結果如圖:
在腳本内添加調試選項,使用set 指令。例如:
要開啟-x選項,則在腳本内容中添加指令:set -x ,對應的set +x 是關閉調試。
#!/bin/bash
for i in {1..6};
do
set -x
echo $i
done
echo "Script executed "
現在要看debug.sh腳本執行的調試資訊,就不需要使用bash -x ./debug.sh執行了。直接./debug.sh 就可以看到調試資訊。結果:
還有一種更便捷的方法,就是在腳本開頭添加-xv選項:
#!/bin/bash -xv
for i in {1..6};
do
echo $i
done
echo "Script executed”
同樣現在執行./debug.sh,也可列印出調試資訊。
前面介紹的調試手段是Bash内建的。它們通常以固定的格式生成調試資訊。但是在很多情況下,我們需要以自定義格式顯示調試資訊。這可以通過傳遞 _DEBUG環境變量來建立這類調試風格。 請看下面的代碼script.sh:
#!/bin/bash
function DEBUG()
{
[ "$_DEBUG" == "on" ] && $@ || :
}
for i in {1..10}
do
DEBUG echo $i
done
可以将調試功能置為"on"來運作上面的腳本:
_DEBUG=on ./script.sh
如果不開啟調試開關,就直接執行./script。
二、函數和參數
inux shell 可以使用者定義函數,然後在shell腳本中可以随便調用。
shell中函數的定義格式如下:
[ function ] funname [()]
{
action;
[return int;]
}
說明:
1、可以帶function fun() 定義,也可以直接fun() 定義,不帶任何參數。
2、參數傳回,可以顯示加:return 傳回,如果不加,将以最後一條指令運作結果,作為傳回值。 return後跟數值n(0-255)
下面的例子hanshu.sh定義了一個函數并進行調用:
隻需要使用函數名就可以調用某個函數: $ fname ; #執行函數
#!/bin/bash
function fname()
{
echo "這個我的第一個函數!!!"
}
echo "---函數開始執行---"
fname
echo "---函數執行完畢---"
結果如下圖:
參數可以傳遞給函數,并由腳本進行通路:
fname arg1 arg2 ; #傳遞參數
以下是函數fname的定義。在函數fname中,包含了各種通路函數參數的方法。
#!/bin/bash
fname()
{
echo $1, $2; #通路參數1和參數2echo "$@";#以清單的方式一次性列印所有參數
echo "$*"; #類似于$@,但是參數被作為單個實體 return 0; #傳回值
return 0;
}
fname 1 2 3
結果如下:
$1是第一個參數。
$2是第二個參數。
$n是第n個參數。
"$@" 被擴充成 "$1" "$2" "$3"等。
"$*" 被擴充成 "$1c$2c$3",其中c是IFS的第一個字元。
"$@" 要比"$*"用得多。由于 "$*"将所有的參數當做單個字元串,是以它很少被使用。
三、将指令序列的輸出讀入變量
shell腳本最棒的特性之一就是可以輕松地将多個指令或工具組合起來生成輸出。一個指令的輸出可以作為另一個指令的輸入,而這個指令的輸出又會傳遞至另一個指令,依次類推。
這種指令組合的輸出可以被存儲在一個變量中。這則攻略将示範如何組合多個指令以及如何讀取其輸出。
(1) 先從組合兩個指令開始:
ls | cat -n > out.txt
ls的輸出(目前目錄内容的清單)被傳給cat -n,後者将通過stdin所接收到輸入内容加上行号,
然後将輸出重定向到檔案out.txt。
(2) 我們可以用下面的方法讀取由管道相連的指令序列的輸出:這種方法被稱為子shell。
例如:cmd_output=$(ls | cat -n)
echo $cmd_output
另一種被稱為反引用(有些人們也稱它為反标記)的方法也可以用于存儲指令輸出。
例如:cmd_output=`ls | cat -n`
echo $cmd_output
有很多種方法可以給指令分組。來看看其中的幾種:
1、利用子shell生成一個獨立的程序子shell本身就是獨立的程序。可以使用()操作符來定義一個子shell
pwd;
(cd /bin; ls);
pwd;
當指令在子shell中執行時,不會對目前shell有任何影響;所有的改變僅限于子shell内。例如:當用cd指令改變子shell的目前目錄時,這種變化不會反映到主shell環境中。
2、通過引用子shell的方式保留白格和換行符
假設我們使用子shell或反引用的方法将指令的輸出讀入一個變量中,可以将它放入雙引号中,以保留白格和換行符(\n)。例如:
out=$(cat text.txt)
echo $out
out="$(cat tex.txt)"
echo$out
四、Read函數
read是一個重要的Bash指令,它用于從鍵盤或标準輸入中讀取文本。
我們可以使用read以互動的形式讀取來自使用者的輸入,不過read能做的可遠不止這些。任何程式設計語言的輸入庫大多都是從鍵盤讀取輸入;但隻有當Enter鍵按下的時候,才标志着輸入完畢。
在有些重要情形下是沒法按Enter鍵的,輸入結束與否是基于字元數或某個特定字元來決定的。
例如,在一個遊戲中,當 按下 + 鍵時,小球就會向上移動。那麼若每次都要按下 + 鍵,然後再按Enter鍵來确認已經按過 + 鍵,這就顯然太低效了。
read指令提供了一種不需要按Enter鍵就能夠搞定這個任務的方法。
你可以借助read指令的各種選項來實作不同的效果。方法如下所示:
(1) 下面的語句從輸入中讀取n個字元并存入變量:
read -n 2 var
echo $var
(2) 用無回顯的方式讀取密碼:
read -s var
(3) 顯示提示資訊:
read -p "Enter input:" var
(4) 在特定時限内讀取輸入:
read -t 2 var #在2秒内将鍵入的字元串,且鍵入回車後,讀入變量var
(5) 用特定的定界符作為輸入行的結束:
read -d ":" var
hello:#var 被設定為 hello
(6)read指令是一個一個詞組地接收輸入的參數,每個詞組需要使用空格進行分隔;如果輸入的詞組個數大于需要的參數個數,則多出的詞組将被作為整體為最後一個參數接收。
read firstStr secondStr
echo "第一個參數:$firstStr; 第二個參數:$secondStr"
執行測試:
./test.sh
一 二 三 四
第一個參數:一; 第二個參數:二 三 四
五、運作指令直至執行成功
在日常工作中使用shell時,有時候指令隻有滿足某些條件或是某種外部事件(例如檔案可以被下載下傳)操作才能夠成功執行。這種情況下,你可能希望重複執行指令,直到成功為止。
重複執行指令函數:
repeat() {while :;do $@ && return; done}
修改間隔時間後再次執行,預設會不斷執行:
repeat() {while :; do sleep 30; $@ && return ; done}
舉例:
#!/bin/bash
repeat()
{
while :;
do
sleep 30 ;
$@ && return ;
done
}
echo "開始計時"
echo `date`
repeat pwd
echo `date`
運作腳本,等待30S後,在執行pwd。
六、字段分隔符和疊代器
内部字段分隔符(Internal Field Separator,IFS)是shell腳本程式設計中的一個重要概念。在處理文本資料時,它的用途可不小。我們将會讨論把單個資料流劃分成不同資料元素的定界符 (delimiter)。内部字段分隔符是用于特定用途的定界符。IFS是存儲定界符的環境變量。它是目前shell環境使用的預設定界字元串。
1、檢視IFS的值
echo “$IFS"
直接輸出IFS是看不到值的,轉化為二進制就可以看到了:
echo "$IFS"|od -b
輸出結果:
0000000 040 011 012 012
0000004
"040"是空格,"011"是Tab,"012"是換行符"\n" 。最後一個 012 是因為 echo 預設是會換行的。
2、IFS的預設值為空白字元(換行符、制表符或者空格)。
當IFS被設定為逗号時,shell将逗号視為一個定界符,是以變量$item在每次疊代中讀取由逗号分隔的子串作為變量值。 如果沒有把IFS設定成逗号,那麼下面的腳本會将全部資料作為單個字元串列印出來。執行個體:
#!/bin/bash
data="name,sex,rollno,location"
old_IFS=$IFS
IFS=","
for item in $data;
do
echo Item: $item
done
IFS=$old_IFS
輸出結果:
Item: name
Item: sex
Item: rollno
Item: location
七、比較與測試
程式中的流程控制是由比較語句和測試語句處理的。Bash同樣具備多種與Unix系統級特性相相容的執行測試的方法。我們可以用if、if else以及邏輯運算符進行測試,用比較運算符來比較資料項。除此之外,還有一個test指令也可以用于測試。
來看看用于比較和測試的各種方法:
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語句可以進行嵌套。if的條件判斷部分可能會變得很長,但可以用邏輯運算符将它變得簡潔一些:
[ condition ] && action; # 如果condition為真,則執行action;
[ condition ] || action; # 如果condition為假,則執行action。
&&是邏輯與運算符,||是邏輯或運算符。編寫Bash腳本時,這是一個很有用的技巧。
1、算數比較
條件通常被放置在封閉的中括号内。一定要注意在[或]與操作數之間有一個空格。如果
忘記了這個空格,腳本就會報錯。
對變量或值進行算術條件判斷:
[ $var -eq 0 ] #當$var 等于0時,傳回真
[ $var -ne 0 ] #當$var 為非0時,傳回真
其他重要的操作符如下所示:
-gt:大于。
-lt:小于。
-ge:大于或等于。
-le:小于或等于。
可以按照下面的方法結合多個條件進行測試:
[ $var1 -ne 0 -a $var2 -gt 2 ] #使用邏輯與-a
[ $var1 -ne 0 -o var2 -gt 2 ] #邏輯或 -o
腳本執行個體:
#!/bin/bash
var=133
if [ $var -ne 0 ]
then
echo True
else
echo Flase
fi
很明顯,結果傳回True.
2、字元串比較
使用字元串比較時,最好用雙中括号,因為有時候采用單個中括号會産生錯誤,是以最好避開它們。
可以用下面的方法檢查兩個字元串,看看它們是否相同。
️注:注意在 = 前後各有一個空格。如果忘記加空格,那就不是比較關系了,而變成了指派語句。
[[ $str1 = $str2 ]]:當str1等于str2時,傳回真。也就是說,str1和str2包含的文本是一模一樣的。
[[ $str1 == $str2 ]]:這是檢查字元串是否相等的另一種寫法。也可以檢查兩個字元串是否不同。
[[ $str1 != $str2 ]]:如果str1和str2不相同,則傳回真。
[[ $str1 > $str2 ]]:如果str1的字母序比str2大,則傳回真。
[[ $str1 < $str2 ]]:如果str1的字母序比str2小,則傳回真。
[[ -z $str1 ]]:如果str1包含的是空字元串,則傳回真。
[[ -n $str1 ]]:如果str1包含的是非空字元串,則傳回真。
#!/bin/bash
str1=abcd
str2=dfgh
if [[ $str1 = $str2 ]]
then
echo True
else
echo Flase
fi
很明顯,結果傳回Flase.
使用邏輯運算符 && 和 || 能夠很容易地将多個條件組合起來。 見執行個體:
#!/bin/bash
str1=abcd
str2=dfgh
if [[ $str1 = $str2 ]] && [[ $str1 != $str2 ]]
then
echo True
else
echo Flase
fi
test指令可以用來執行條件檢測。用test可以避免使用過多的括号。見執行個體:
if [ $var -eq 0 ]; then echo "True"; fi
也可以寫成:
if test $var -eq 0 ; then echo "True"; fi
3、檔案系統相關測試
我們可以使用不同的條件标志測試不同的檔案系統相關的屬性。
[ -f $file_var ]:如果給定的變量包含正常的檔案路徑或檔案名,則傳回真。
[ -x $var ]:如果給定的變量包含的檔案可執行,則傳回真。
[ -d $var ]:如果給定的變量包含的是目錄,則傳回真。
[ -e $var ]:如果給定的變量包含的檔案存在,則傳回真。
[ -c $var ]:如果給定的變量包含的是一個字元裝置檔案的路徑,則傳回真。
[ -b $var ]:如果給定的變量包含的是一個塊裝置檔案的路徑,則傳回真。
[ -w $var ]:如果給定的變量包含的檔案可寫,則傳回真。
[ -r $var ]:如果給定的變量包含的檔案可讀,則傳回真。
[ -L $var ]:如果給定的變量包含的是一個符号連結,則傳回真。
#!/bin/bash
fpath="/etc/passwd"
if [ -e $fpath ]; then
echo File exists;
else
echo Does not exist;
fi
以上,未完待續~~
作者:
擱淺出處:
http://www.cnblogs.com/xiaoxi-3-/如果對您有幫助,請關注我的同名簡書:
https://www.jianshu.com/u/da1677475c27本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。