一、select循環
功能:主要用于創建菜單,菜單按數字順序排列。并將PS3變量的值用作用戶輸入提示。用戶的選擇被保存在內置變量REPLY中。也可以和case語句結合,在select循環中對用戶的輸入作出判斷并處理。
注意:select循環為無限循環,因此需要給出循環退出條件。
例1:讓用戶選擇其來自哪個省市
#!/bin/bash # echo "where are you come from" PS3="i came from :" declare -a cm=('beijing' 'shanghai' 'hainan' 'hebei' 'hunan') select path in ${cm[@]} do break done echo "you are from $path"
例2:詢問用戶是哪個足球俱樂部的球迷,并顯示該俱樂部的教練。當用戶輸入quit時,退出。
#!/bin/bash # echo "Which football club is your favorite(quit to exit):" PS3="my favorite is : " select path in 'ManUnited' 'Barcelona' 'Chelsea' 'RealMadrid' 'quit' do case $path in ManUnited) echo "you are $path fans" echo "$path coah is Jose mourinho" ;; Barcelona) echo "you are $path fans" echo "$path coah is Enrique" ;; Chelsea) echo "you are $path fans" echo "$path coah is Conti" ;; RealMadrid) echo "you are $path fans" echo "$path coah is Zidane" ;; quit) echo "See you..." exit ;; *) echo "Unknown option." continue ;; esac done
二、函數
介紹:函數是由若干條shell命令組成的語句塊,實現代碼重用和模塊化編程。
應用場景:在編寫shell腳本時,有些代碼會被反復重用多次,把這些可能重復使用的代碼寫成函數,這樣就可以通過函數名調用來高效、重復地利用。另外,當shell腳本功能比較復雜時,可以將腳本劃分為多個模塊,不同模塊可以寫成不同的函數。
2.1函數的語法格式:
格式1、
f_name () {
函數體…
}
格式2、
function f_name {
函數體…
}
2.2函數的生命周期:被調用時創建,返回時終止。
2.3函數的調用:
函數要能被調用,需在調用前進行定義。調用時直接鍵入函數名即可。例:
若不調用函數,則函數不會被執行。
2.4函數的返回值:
分為兩種
a、正常返回的數值:
(1)函數中的打印語句,如echo、print
(2)函數中命令的執行結果
示例1:函數執行成功之后,會將用戶輸入的指令輸出
如何獲取函數的輸出。有時候我們需要對函數正常輸出的數值進行處理,這時可以通過下面的方式獲取函數的輸出
示例2:將函數執行之后的輸出保存,進行處理
b、執行狀態返回值:
(1)取決于函數中執行的最后一條語句的執行狀態
(2)也可通過return自定義
return N(0-255)
在示例3中,可以觀察到,函數的執行狀態取決于函數中執行的最后一條語句的執行狀態;但是沒辦法判斷函數中的其他語句是否執行成功,當需要根據函數中不同的語句的執行狀態做出判斷;來決定后續的操作時,需要用到return。如示例4。
#!/bin/bash # user_input () { read -p "please enter an excuteable command : " cmd echo $cmd ls /etc/justfortest } user_input echo "Funtion status is $?"
在示例4中,需要判斷用戶輸入的用戶是否存在,存在時,返回5的狀態值;用戶不存在時,添加用戶,根據添加用戶的命令的執行狀態來判斷添加是否成功。
2.5在腳本中調用函數:
在一個腳本中想要調用寫在另一個腳本中的函數,同過source ”腳本名“來實現
cacufun.sh腳本內容如下:cacufun.sh定義了加減乘除4中運算的函數
#!/bin/bash # addtion_num() { local res=`echo "$1 $2 $3"| bc` echo "result is $res" } subtract_num() { local res=`echo "$1 $2 $3"| bc` echo "result is $res" } multiplication_num() { local res=`echo "$1 $2 $3"| bc` echo "result is $res" } devision_num() { local res=`echo "$1 $2 $3"| bc` echo "result is $res" }
在cacutest.sh腳本中調用cacufun.sh腳本中的函數,實現對用戶輸入數據的運算。
#!/bin/bash # source /root/bin/practice/cacufun.sh read -p "enter two int number to caculate: " num1 num2 num3 case $num2 in +) echo "$num1 $2 $num3 " `addtion_num $num1 $num2 $num3` ;; -) echo "$num1 $2 $num3 " `substract_num $num1 $num2 $num3` ;; *) echo "$num1 $2 $num3 " `multiplication_num $num1 $num2 $num3` ;; /) cho "$num1 $2 $num3 " `devision_num $num1 $num2 $num3` ;; *) echo "wrong option" exit 123 esac
2.6函數參數:
函數可以使用類似于調用腳本位置參數來調用函數的參數
函數的參數表示:
$1,$2
$#
$@,$*
函數中的$1,$2…指的是傳遞給函數的參數,而不是傳遞個腳本的參數
2.7 shell腳本中的變量作用域:
1、在函數中使用了在主程序中聲明的變量,重新賦值會直接修改主程序中的變量。
2、如果想讓函數中的變量與主程序中的變量沖突,在函數中聲明變量時,使用local修飾,將變量聲明為局部變量,其作用域僅限于當前函數。
3、變量被聲明的位置決定了其作用域,
3.1 在函數中使用了在主程序中沒有聲明的變量,在函數執行結束后即被撤銷,無論是否使用local修飾符
3.2 在函數中聲明了主程序中聲明過的變量,這兩個變量為不同的變量。函數中的變量在執行結束后即被撤銷。
2.8 shell腳本中變量的查找順序:
1、內層函數本身
2、外層還是定義的
3、主程序
4、shell環境變量
2.9 函數的遞歸:
函數直接或調用自身
示例:
斐波那契數列又稱黃金分割數列,因數學家列昂納多· 斐波那契以兔子繁殖為例子而引入,故又稱為“兔子數列”,指的是這樣一個數列:0 、1、1 、2 、3 、5 、8 、13 、21 、34 、…… ,斐波納契數列以如下被以遞歸的方法定義:F (0 )=0 ,F (1 )=1 ,F (n )=F(n-1)+F(n-2) (n≥2) )
寫一個函數,求n階斐波那契數列
#!/bin/bash # fab () { if [[ $1 -eq 1 || $1 -eq 2 ]];then echo -n "1 " else echo -n "$[$(fab $[$1-1])+$(fab $[$1-2])] " fi } for i in $(seq 1 $1) do fab $i done echo
三、實戰演練
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 # srv_file=/var/lock/subsys/$0 srv_name=`basename $0` usage_srv() { if [[ $1 -lt 1 || $1 -ge 2 ]];then echo "Wrong option:" echo "Usage : $srv_name start|stop|restart|status" exit 123 fi } start_srv() { if [ -f $srv_file ];then echo "service $srv_name already started" else touch $srv_file && echo "start $srv_name finished" fi } stop_srv() { if [ ! -f $srv_file ];then echo "service $srv_name already stopped" else rm -f $srv_file &>/dev/null && echo "stop $srv_name finished" fi } status_srv() { if [ -f $srv_file ];then echo "service $srv_name is running" else echo "service $srv_name was stopped" fi } case $1 in start) start_srv $1 ;; stop) stop_srv $1 ;; restart) stop_srv $1 start_srv $1 ;; status) status_srv $1 ;; *) usage_srv $# ;; esac
2、編寫腳本/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 # f_dir=/mnt/sysroot cmd_input () { while true do read -p "Please input an excuteable command to backup (quit to exit): " cmd if [[ -z $cmd ]];then echo "wrong option,try again..." continue elif [[ $cmd == "quit" ]];then exit 88 elif ! which --skip-alias $cmd &>/dev/null;then echo "No such command,try again..." continue else break fi done } cp_cmd() { cmd_path=`which --skip-alias $cmd` cmd_dir=`dirname $cmd_path` [ ! -d $f_dir$cmd_dir ] && mkdir -p $f_dir$cmd_dir cp $cmd_path $f_dir$cmd_dir && echo "backup $cmd finished" } cp_cmd_lib() { cmd_lib=`ldd $cmd_path` for path in $cmd_lib do cmd_lib_dir=`echo $path | grep -o -E '/[^[:space:]]+.*/'` if [[ ! -d $f_dir$cmd_lib_dir ]];then mkdir -p $f_dir$cmd_lib_dir && cp $path $f_dir$cmd_lib_dir &> /dev/null else cp $path $f_dir$cmd_lib_dir &> /dev/null fi done echo "backup ${cmd}'s library finished" } main() { while true do cmd_input cp_cmd cp_cmd_lib done } main
原創文章,作者:M20-1鐘明波,如若轉載,請注明出處:http://www.www58058.com/38011
總結的很好,態度很端正,加油。
@馬哥教育:好的,謝謝