概述
承接上篇,繼續介紹一下另一個循環語句select,還有腳本中函數的相關內容,分為三個部分:
1、select語句的介紹和效果演示
2、腳本函數詳解
3、函數效果舉例
第一章 select語句的介紹和效果演示
1、select 循環與菜單格式
select VAR in LIST;do
循環體命令
done
select 循環主要用于創建菜單,按數字順序排列的菜單項將顯示在標準錯誤上,并顯示 PS3 提示符,等待用戶輸入
用戶輸入菜單列表中的某個數字,執行相應的命令
用戶輸入被保存在內置變量 REPLY 中
2、select 與 case
select 是個無限循環,因此要記住用 break 命令退出循環,或用 exit 命令終止腳本。也可以按 ctrl+c退出循環
select 經常和 case 聯合使用
與 for 循環類似,可以省略 in list , 此時使用位置參量
3、selcet應用舉例
4、補充:腳本執行時幾種執行方式的區別
.和source來執行腳本與bash 和給腳本執行權限執行的區別:
. 和source執行腳本時:沒有開子進程執行腳本,相當于直接在shell里面運行腳本里面的語句,故運行完腳本,腳本里面的變量在shell里依然有效
bash或執行權限執行腳本:開了個子進程執行腳本,故腳本里面定義的變量,無法在shell里面繼續生效
第二章 腳本中函數詳解
1、函數的基本概念
函數function是由若干條shell命令組成的語句塊,實現代碼重用和模塊化編程。
它與shell程序形式上是相似的,不同的是它不是一個單獨的進程,不能獨立運行,而是shell程序的一部分。
注意:定義函數的代碼段不會自動執行,在調用時才執行
2、函數和shell程序比較相似,區別在于:
Shell程序在子Shell中運行
而Shell函數在當前Shell中運行。因此在當前Shell中,函數可以對shell中變量進行修改
函數在腳本中,相當于在腳本中插入了一段代碼,所以函數并不是在腳本的shell中又開一個子shell執行函數,而是跟腳本shell在同一個shell中
3、函數的定義:
語法一:
function FUNCTION_NAME {
函數體
}
語法二:
FUNCTION_NAME() {
函數體
}
函數的定義位置:
可在交互式環境下定義函數
可將函數放在腳本文件中作為它的一部分
可放在只包含函數的單獨文件中
4、函數的調用
在代碼中給定函數名即可,函數名出現的任何位置,在代碼執行時,都會被自動替換為函數代碼
函數的生命周期:被調用時創建,返回時終止
可以將經常使用的函數存入函數文件,然后將函數文件載入shell。文件名可任意選取,但最好與相關任務有某種聯系。例如: functions.main,一旦函數文件載入shell,就可以在命令行或腳本中調用函數。可以使用set命令查看所有定義的函數,其輸出列表包括已經載入shell的所有函數。若要改動函數,首先用unset命令從shell中刪除函數。改動完畢后,再重新載入此文件
載入文件的方式:. FILENAME或source FILENAME
5、函數的返回值
函數有兩種返回值:
函數的執行結果返回值:
<1> 使用echo或printf命令進行輸出
<2> 函數體中調用命令的輸出結果
函數的退出狀態碼:
<1> 默認取決于函數中執行的最后一條命令的退出狀態碼
<2> 自定義退出狀態碼, 其格式為:
return 從函數中返回,用最后狀態命令決定返回值
return 0 無錯誤返回。
return 1-255 有錯誤返回
return其功能與腳本中exit的功能類似,在函數運行的任何地方,出現return即代表函數終止,即便return的位置不是最后一行,也會退出整個函數,功能與腳本的exit一樣,腳本中一旦遇到exit,就退出腳本的運行
6、刪除函數
unset命令完成此功能.命令格式為:unset FUNCTION_NAME
7、函數中的參數
函數可以接受參數:
傳遞參數給函數:調用函數時,在函數名后面以空白分隔,給定參數列表即可;例如 FUNCTION_NAME arg1 arg2 …
在函數體中當中,可使用$1, $2, …調用這些參數;還可以使用$@, $*, $#等特殊變量
8、函數中的變量
變量作用域:
環境變量:當前shell和子shell有效
本地變量:只在當前shell進程有效,為執行腳本會啟動專用子shell進程;因此,本地變量的作用范圍是當前shell腳本程序文件,包括腳本中的函數。
局部變量:函數的生命周期;函數結束時變量被自動銷毀
定義局部變量的方法:在函數體中,使用local VAR=VALUE
這樣定義之后,該變量的作用范圍就僅僅在函數體內部生效,函數結束,則變量不會影響后續腳本中變量的運行
注意:如果函數中有局部變量,如果其名稱同本地變量, 使用局部變量
9、函數的遞歸
函數的遞歸:函數直接或間接調用自身
例如:數學中的階乘
N!=N*(N-1)!=N*(N-1)*(N-2)!=…
這樣就可以用函數的遞歸進行計算
jc() {
if [ $1 -eq 0 -o $1 -eq 1 ];then
echo 1
else
echo "$[$1*`jc $[$1-1]`]"
fi
}
例如:斐波那契數列
1,1,2,3,5,8,13,21…
第一個值為1,第二個值為1,隨后的每一個值都為其前兩個數字之和
求斐波那契數列的第N個數字是多少:(N-1)+(N-2)
fblq() {
if [ $1 -eq 1 -o $1 -eq 2 -o $1 ];then
echo 1
else
echo "$[`fblq $[$1-1]`+`fblq $[$1-2]`]"
fi
}
第三章 函數效果舉例
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為當前腳本名
(7)在所有模式下禁止啟動該服務,可用chkconfig 和 service命令管理
#!/bin/bash # Autor: nwc # Version: 2.0 # CreateTime: 2016-08-19 # Description: # 關于第7條的實現,考慮可以將腳本放到/etc/init.d目錄下,在腳本開頭注釋段標明“# chkconfig:運行級別 啟動優先級 關閉優先級” # 然后運行chkconfig --add 腳本名 --level 指定哪些級別開機啟動 FILE=/tmp/$0 START() { if [ -f $FILE ];then echo "$0 has been start" else touch $FILE && echo "Start success" || echo "Start fail" fi } STOP() { if [ -f $FILE ];then rm -rf $FILE && echo "Stop success" || echo "Stop fail" else echo "$0 has been stopped" fi } RESTART() { STOP START } STATUS() { [ -f $FILE ] && echo "$0 is running..." || echo "$0 is stopped..." } read -p "Please input you choice start|stop|restart|status: " CH case $CH in start) START ;; stop) STOP ;; restart) RESTART ;; status) STATUS ;; *) echo "bad choice..." ;; 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 # Autor: nwc # Version: 2.0 # CreateTime: 2016-08-19 # Description: CMDCP() { DIR=`dirname $CmdPath` if [ -d ${LuJing}${DIR} ] ;then cp $CmdPath ${LuJing}${DIR} && echo "cp $CMD success" || echo "cp $CMD fail" else mkdir -p ${LuJing}${DIR} cp $CmdPath ${LuJing}${DIR} && echo "cp $CMD success" || echo "cp $CMD fail" fi } CMDLIBCP() { for i in ${LibCmd} ; do LIBDIR=`dirname $i` if [ -d ${LuJing}${LIBDIR} ];then cp $i ${LuJing}${LIBDIR} && echo "cp $i success" || echo "cp $i fail" else mkdir -p ${LuJing}${LIBDIR} cp $i ${LuJing}${LIBDIR} && echo "cp $i success" || echo "cp $i fail" fi done } while true;do read -p "Input a exec program name like ls/cat... q for quit " CMD case $CMD in q) echo "exit..." exit ;; *) if which --skip-alias $CMD &>/dev/null && [[ $CMD != "q" ]]; then CmdPath=`which --skip-alias $CMD` LibCmd=`ldd $CmdPath|grep -o -E "/[^[:space:]]+"` LuJing=/tmp/command CMDCP CMDLIBCP else echo "you input is not a right command" continue fi ;; esac done
執行效果如下:
原創文章,作者:M20-1倪文超,如若轉載,請注明出處:http://www.www58058.com/37714
總結的很詳細,作業也完成的很用心,關于shell腳本主要是多抽時間寫,看,學習別人的思想,只有養成了編程的思想,才能寫出優秀的代碼。