shell腳本編程詳解3:
在前兩節我們分別介紹了shell腳本編程的基礎和循環,判斷的知識,今天我們分享腳本的另一種用法,函數.
概論:
函數function是由若干條shell命令組成的語句塊,實現代碼重用和模塊化編程。
它與shell程序形式上是相似的,不同的是它不是一個單獨的進程,不能獨立運行,而是shell程序的一部分。
函數和shell程序比較相似,區別在于:
Shell程序在子Shell中運行
而Shell函數在當前Shell中運行。因此在當前Shell中,函數可以對shell中變量進行修改
使用:
語法:
函數由兩部分組成:函數名和函數體。
語法一:
function f_name {
…函數體…
}
語法二:
f_name() {
…函數體…
}
定義:
可在交互式環境下定義函數
可將函數放在腳本文件中作為它的一部分
可放在只包含函數的單獨文件中
調用:函數只有被調用才會執行;
調用:給定函數名
函數名出現的地方,會被自動替換為函數代碼
函數的生命周期:被調用時創建,返回時終止
返回值:
函數有兩種返回值:
函數的執行結果返回值:
(1) 使用echo或printf命令進行輸出
(2) 函數體中調用命令的輸出結果
函數的退出狀態碼:
(1) 默認取決于函數中執行的最后一條命令的退出狀態碼
(2) 自定義退出狀態碼, 其格式為:
return 從函數中返回,用最后狀態命令決定返回值
return 1-255 有錯誤返回
交互式使用函數:
示例:
$dir() {
> ls -l
> }
定義該函數后,若在$后面鍵入dir,其顯示結果同ls -l的
作用相同。
$dir
? 該dir函數將一直保留到用戶從系統退出,或執行了如下
所示的unset命令:
$ unset dir
腳本使用函數:
函數在使用前必須定義,因此應將函數定義放在腳本開始部分,直至
shell首次發現它后才能使用
調用函數僅使用其函數名即可。
示例:
$cat func1 #!/bin/bash # func1 hello() { echo "Hello there today's date is `date +%F`" } echo "now going to the function hello" hello echo "back from the function"
函數文件:
可以將經常使用的函數存入函數文件,然后將函數文件載入shell。
文件名可任意選取,但最好與相關任務有某種聯系。例如: functions.main
一旦函數文件載入shell,就可以在命令行或腳本中調用函數。可以使用set命令查看所有定義的函數,其輸出列表包括已經載入shell的所有函數。
若要改動函數,首先用unset命令從shell中刪除函數。改動完畢后,再重新載入此文件
創建函數文件:
$cat functions.main #!/bin/bash #functions.main findit() { if [ $# -lt 1 ] ; then echo "Usage:findit file" return 1 fi find / -name $1 –print }
載入函數:
函數文件已創建好后,要將它載入shell
定位函數文件并載入shell的格式:
. filename 或 source filename
注意:此即<點> <空格> <文件名>
這里的文件名要帶正確路徑
示例:上例中的函數,可使用如下命令:
$ . functions.main
檢查載入函數:
使用set命令檢查函數是否已載入。 set命令將在shell中顯示所有的載入函數。
示例:
$set findit=( ) { if [ $# -lt 1 ]; then echo "usage :findit file"; return 1 fi find / -name $1 -print } …
執行shell函數:
要執行函數,簡單地鍵入函數名即可:
示例:
$findit groups /usr/bin/groups /usr/local/backups/groups.bak
刪除shell函數:
現在對函數做一些改動。首先刪除函數,使其對shell不可用。使用unset命令完成此功能.
命令格式為:
unset function_name
實例:
$unset findit
再鍵入set命令,函數將不再顯示
函數參數:
函數可以接受參數:
傳遞參數給函數:調用函數時,在函數名后面以空白分隔
給定參數列表即可;例如“ testfunc arg1 arg2 …”
在函數體中當中,可使用$1, $2, …調用這些參數;還
可以使用$@, $*, $#等特殊變量
函數變量:
變量作用域:
環境變量:當前shell和子shell有效
本地變量:只在當前shell進程有效,為執行腳本會啟動
專用子shell進程;因此,本地變量的作用范圍是當前shell腳本
程序文件,包括腳本中的函數。
局部變量:函數的生命周期;函數結束時變量被自動銷毀
注意:如果函數中有局部變量,如果其名稱同本地變量, 使用局部變量。
在函數中定義局部變量的方法
local NAME=VALUE
函數遞歸:
函數遞歸:
函數直接或間接調用自身
注意遞歸層數
遞歸實例:
階乘是基斯頓·卡曼于 1808 年發明的運算符號,是數學術語
一個正整數的階乘( factorial)是所有小于及等于該數的正整
數的積,并且有0的階乘為1。自然數n的階乘寫作n!。
n!=1×2×3×…×n。
階乘亦可以遞歸方式定義: 0!=1, n!=(n-1)!×n。
n!=n(n-1)(n-2)…1
n(n-1)! = n(n-1)(n-2)!
栗子:
fact.sh #!/bin/bash # fact() { if [ $1 -eq 0 -o $1 -eq 1 ]; then echo 1 else echo $[$1*$(fact $[$1-1])] fi } fact 5
實戰:
1.加減乘除函數,腳本調用
#!/bin/bash jia(){ sum=0 sum=$[$1+$2] echo $sum } jian(){ sum=0 sum=$[$1-$2] echo $sum } cheng(){ sum=0 sum=$[$1*$2] echo $sum } chu(){ sum=0 sum=$[$1/$2] echo $sum } #!/bin/bash #**************************************************** #Description: 測試加減乘除函數腳本 #**************************************************** . sum.sh echo ' 6=$1,3=$2' jia 6 3 jian 6 3 cheng 6 3 chu 6 3
2.管理服務腳本
1、編寫服務腳本/root/bin/testsrv.sh,完成如下要求
(1) 腳本可接受參數:start, stop, restart, status
(2) 如果參數非此四者之一,提示使用格式后報錯退出
(3) 如是start:則創建/var/lock/subsys/SCRIPT_NAME, 并顯示“啟動成功”
考慮:如果事先已經啟動過一次,該如何處理?
(4) 如是stop:則刪除/var/lock/subsys/SCRIPT_NAME, 并顯示“停止完成”
考慮:如果事先已然停止過了,該如何處理?
(5) 如是restart,則先stop, 再start
考慮:如果本來沒有start,如何處理?
(6) 如是status, 則如果/var/lock/subsys/SCRIPT_NAME文件存在,則顯示“SCRIPT_NAMEis running…”
如果/var/lock/subsys/SCRIPT_NAME文件不存在,則顯示“SCRIPT_NAME is stopped…”
其中:SCRIPT_NAME為當前腳本名
#!/bin/bash # #chkconfig:35 66 33 #description:this is myservice lock=/var/lock/subsys/myservice start(){ if [ ! -e $lock ];then touch $lock echo -e "start myservice [ \033[32mOK\033[0m ]" else echo -e "start myservice [ \033[31mFAILED\033[0m ]" fi } stop(){ if [ -e $lock ];then rm -rf $lock echo -e "stop myservice [ \033[32mOK\033[0m ]" else echo -e "stop myservice [ \033[31mFAILED\033[0m ]" fi } status(){ if [ -e $lock ];then echo "myservice is running" else echo "myservice alread stopped" fi } case $i in start) start;; stop) stop;; restart) stop start;; status) status;; *) echo "Usage: `basename $0` start|stop|restart|status";; esac
3
編寫腳本/root/bin/copycmd.sh
(1) 提示用戶輸入一個可執行命令名稱;
(2) 獲取此命令所依賴到的所有庫文件列表
(3) 復制命令至某目標目錄(例如/mnt/sysroot)下的對應路徑下;
如:/bin/bash ==> /mnt/sysroot/bin/bash
/usr/bin/passwd==> /mnt/sysroot/usr/bin/passwd
(4) 復制此命令依賴到的所有庫文件至目標目錄下的對應路徑下:
如:/lib64/ld-linux-x86-64.so.2 ==> /mnt/sysroot/lib64/ld-linux-x86-64.so.2
(5)每次復制完成一個命令后,不要退出,而是提示用戶鍵入新的要復制的命令,并重復完成上述功能;直到用戶輸入quit退出
#!/bin/bash copy(){ if [ ! -e $dir$cmddir_path ];then mkdir -p $dir$cmddir_path &>/dev/null cp -r $cmddir $dir$cmddir_path &>/dev/null echo " $cmd file no" elif [ -e $dir$cmddir ];then echo " $cmd file is exit!!!" elif [ -e $dir$cmddir_path ];then cp -r $cmddir $dir$cmddir_path echo "$cmd file copy over!!" else echo "A" fi } copylib(){ for i in $libdir do lib=`echo $i|sed 's@.*=>@@'| tr -s "\t" " "|cut -d' ' -f2` lib_path=`echo $i|sed 's@[^/]\+$@@'` if [ ! -e $lib ];then echo "$lib is no exit" elif [ ! -e $dir$lib_path ];then mkdir -p $dir$lib_path &>/dev/null cp $lib $dir$lib_path &>/dev/null echo "$cmd lib dir is no ,mkdir filedir,and copy libfile" elif [ -e $dir$lib_path ];then cp $lib $dir$lib_path &>/dev/null echo "$cmd lib file copy over!!!" fi done } while true do read -p "Please Enter A Command(quit:exit the script) :" cmd dir=/mnt/sysroot cmddir=`which --skip-alias $cmd` cmddir_path=`echo $cmddir|sed 's@[^/]\+$@@'` libdir=`ldd $cmddir` if [ -z $cmd ];then echo " at least enter a cmd!!!" continue elif [ "$cmd" == "quit" ];then echo " soon exit a script " exit 2 elif [! $cmddir &>/dev/null ];then echo "this commend is wrong,again try..." continue else echo "copt $cmd started at once" #copy copylib fi done
原創文章,作者:wencx,如若轉載,請注明出處:http://www.www58058.com/37729
shell腳本函數不僅能實現代碼重用,還能提高運行效率。linux中很多程序,都是使用函數來進行使用和調用的,所謂的組合小程序,完成復雜任務就是這樣的思想。希望下來能多加練習,培養出模塊化的思想哦。