s h e l l允許將一組命令集或語句形成一個可用塊,這些塊稱為s h e l l函數。
函數體。
標題是函數名。函數體是函數內的命令集合。標題名應該唯一;如果不是,將會混淆結
果,因為腳本在查看調用腳本前將首先搜索函數調用相應的s h e l l。
定義函數的格式為:
函數名()
{
命令1
. . .
}
或者
函數名(){
命令1
. . .
}
兩者方式都可行。如果願意,可在函數名前加上關鍵字f u n c t i o n,這取決於使用者。
f u n c t i o n 函數名()
{ ...
}
可以將函數看作是腳本中的一段代碼,但是有一個主要區別。執行函數時,它保留當前
s h e l l和內存信息。此外如果執行或調用一個腳本文件中的另一段代碼,將創建一個單獨的
s h e l l,因而去除所有原腳本中定義的存在變量。
函數可以放在同一個文件中作為一段代碼,也可以放在只包含函數的單獨文件中。函數
不必包含很多語句或命令,甚至可以只包含一個e c h o語句,這取決於使用者。
所有函數在使用前必須定義。這意味著必須將函數放在腳本開始部分,直至s h e l l解釋器
首次發現它時,才可以使用。調用函數僅使用其函數名即可。
向函數傳遞參數就像在一般腳本中使用特殊變量$ 1 , $ 2 . . . $ 9一樣,函數取得所傳參數後,
將原始參數傳回s h e l l腳本,因此最好先在函數內重新設置變量保存所傳的參數。這樣如果函
數有一點錯誤,就可以通過已經本地化的變量名迅速加以跟蹤。函數裡調用參數(變量)的
轉換以下劃線開始,後加變量名,如: _ F I L E N A M E或_ f i l e n a m e。
當函數完成處理或希望函數基於某一測試語句返回時,可做兩種處理:
1) 讓函數正常執行到函數末尾,然後返回腳本中調用函數的控制部分。
2) 使用r e t u r n返回腳本中函數調用的下一條語句,可以帶返回值。0為無錯誤,1為有錯誤。
這是可選的,與最後狀態命令報表例子極其類似。其格式為:
r e t u r n 從函數中返回, 用最後狀態命令決定返回值。
Return 0 無錯誤返回。
Return 1 有錯誤返回
可以直接在腳本調用函數語句的後面使用最後狀態命令來測試函數調用的返回值。
[root@localhost huangcd]# cat test.txt
#!/bin/bash
hello()
{
echo "hello there today's date is `date`"
}
hello
if [ $?=0 ]
then echo "ok"
else
echo "wrong"
fi
[root@localhost huangcd]# sh test.txt
hello there today's date is 2013年 12月 11日 星期三 09:13:31 CST
ok
當你收集一些經常使用的函數時,可以將之放入函數文件中並將文件載入s h e l l。
文件頭應包含語句# ! / b i n / s h,文件名可任意選取,但最好與相關任務有某種實際聯系。例
如,f u n c t i o n s . m a i n。
一旦文件載入s h e l l,就可以在命令行或腳本中調用函數。可以使用s e t命令查看所有定義
的函數。輸出列表包括已經載入s h e l l的所有函數。
如果要改動函數,首先用u n s e t命令從s h e l l中刪除函數,盡管u n s e t刪除了函數以便於此函
數對於s h e l l或腳本不可利用,但並不是真正的刪除。改動完畢後,再重新載入此文件。有些
s h e l l會識別改動,不必使用u n s e t命令,但為了安全起見,改動函數時最好使用u n s e t命令。
下面創建包容函數的函數文件並將之載入s h e l l,進行測試,再做改動,之後再重新載入。
函數文件名為f u n c t i o n s . m a i n,內容如下:
上述腳本本書前面用過,現在將之轉化為一個函數。這是一個基本f i n d命令的前端。如果
不加參數,函數將返回1,即發生錯誤。注意錯誤語句中用到了實際函數名,因為這裡用$ 0,
s h e l l將只返回s h -信息,原因是文件並不是一個腳本文件。這類信息對用戶幫助不大。
[root@localhost huangcd]# cat functions.main
#!/bin/bash
findit(){
if[ $# -lt 1 ]
then echo "usage:findit file"
retrun 1
fi
find / -name $1 -print
}
定位文件格式為:
. / p a t h n a m e / f i l e n a m e
現在文件已經創建好了,要將之載入s h e l l,試鍵入:
$. functions.main
如果返回信息file not found,再試:
$. /functions.main
此即<點> <空格> <斜線> <文件名>,現在文件應該已載入s h e l l。如果仍有錯誤,則應該仔
細檢查是否鍵入了完整路徑名。
[root@localhost huangcd]# . functions.main
使用s e t命令確保函數已載入。s e t命令將在s h e l l中顯示所有的載入函數。
[root@localhost huangcd]# set
BASH=/bin/bash
BASH_ARGC=()
BASH_ARGV=()
BASH_LINENO=()
tmpid=0
findit ()
{
if [ $# -lt 1 ]; then
echo "usage:findit file";
return 1;
fi;
find / -name $1 -print
}
要執行函數,簡單地鍵入函數名即可。這裡是帶有一個參數的f i n d i t函數,參數是某個系
統文件。
[root@localhost huangcd]# findit huangcd
/home/huangcd
[2]+ Stopped find / -name $1 -print
[root@localhost huangcd]# findit
usage:findit file
現在對函數做一些改動。首先刪除函數,使其對s h e l l不可利用。使用u n s e t命令完成此功
能。刪除函數時u n s e t命令格式為:unset function_name
[root@localhost huangcd]# unset findit
編輯函數f u n c t i o n s . m a i n,加入f o r循環以便腳本可以從命令行中讀取多個參數。改動後函
數腳本如下:
[root@localhost huangcd]# cat functions.main
#!/bin/bash
findit(){
if [ $# -lt 1 ]; then
echo "usage:findit file"
return 1
fi
for loop
do
find / -name $1 -print
done
}
再次定位函數
[root@localhost huangcd]# . functions.main
使用s e t命令查看其是否被載入,可以發現s h e l l正確解釋f o r循環以接受所有輸入參數。
[root@localhost huangcd]# set
tmpid=0
findit ()
{
if [ $# -lt 1 ]; then
echo "usage:findit file";
return 1;
fi;
for loop in "$@";
do
find / -name $1 -print;
done
}
現在執行改動過的f i n d i t函數,輸入兩個參數:
[root@localhost huangcd]# findit huangcd
/home/huangcd
/var/spool/mail/huangcd
/var/run/sudo/huangcd
/home/huangcd
/var/spool/mail/huangcd
/var/run/sudo/huangcd
You have new mail in /var/spool/mail/root
既然已經學習了函數的基本用法,現在就用它來做一些工作。函數可以節省大量的編程
時間,因為它是可重用的。
要求輸入字符必須只包含字母。如果不用函數實現這一點,要寫大量腳本。使用函數可
以將重復腳本刪去。這裡用a w k語言測試字符。以下是取得只有小寫或大寫字符的測試函數
首先設置變量$ 1為一有意義的名字,然後用a w k測試整個傳送記錄只包含字母,此命令輸
出(1為非字母,空為成功)保存在變量_ L E T T E R S O N LY中。
然後執行變量測試,如果為空,則為成功,如果有值,則為錯誤。基於此項測試,返回
碼然後被執行。在對腳本的函數調用部分進行測試時,使用返回值會使腳本清晰易懂。
使用i f語句格式測試函數功能:
如果有錯誤,可編寫一個函數將錯誤反饋到屏幕上。
函數n a m e e r r o r用於顯示所有無效輸入錯誤。使用特殊變量$ @顯示所有參數,這裡為變
量F N A M E和S N A M E值。完成腳本如下:
[root@localhost huangcd]# cat func2 #!/bin/bash char_name(){ # check if $1 indeed contain only characters a-z,A-Z _LETTERS_NOLY=$1 _LETTERS_ONLY=`echo $1|awk '{if($0~/[^a-zA-Z]/) print "1"}'` if[ "$_LETTERS_ONLY" != "" ] then return 1 else return 0 fi } name_error() { echo "$@ contains errors,it must contain only letters" } while: do echo -n "what is your first name:" read F_NAME if char_name $F_NAME then break else name_error $F_NAME fi done while: do echo -n "what is your surname:" read S_NAME if char_name $S_NAME then break else name_error $S_NAME fi done