函數介紹
函數function是由若干條shell命令組成的語句塊,實現代碼 重用和模塊化編程 它與shell程序形式上是類似的,不一樣的是它不是一個單獨的 進程,不能獨立運行,而是shell程序的一部分
函數和shell程序比較類似,區別在於:
Shell程序在子Shell中運行
而Shell函數在當前Shell中運行。所以在當前Shell中,函 數能夠對shell中變量進行修改linux
定義函數
函數由兩部分組成:函數名和函數體 shell
語法一: f_name (){ ...函數體... } 語法二: function f_name { ...函數體... } 語法三: f unction f_name () { ...函數體... }
function的幫助信息編程
root@(6-1)app# help function function: function name { COMMANDS ; } or name () { COMMANDS ; } Define shell function. Create a shell function named NAME. When invoked as a simple command, NAME runs COMMANDs in the calling shell's context. When NAME is invoked, the arguments are passed to the function as $1...$n, and the function's name is in $FUNCNAME. Exit Status: Returns success unless NAME is readonly.
函數使用
函數的定義和使用:
可在交互式環境下定義函數
可將函數放在腳本文件中做爲它的一部分
可放在只包含函數的單獨文件中
調用:函數只有被調用纔會執行
調用:給定函數名
函數名出現的地方,會被自動替換爲函數代碼
函數的生命週期:被調用時建立,返回時終止centos
函數有兩種返回值:
函數的執行結果返回值:
(1) 使用echo等命令進行輸出
(2) 函數體中調用命令的輸出結果
函數的退出狀態碼:
(1) 默認取決於函數中執行的最後一條命令的退出狀態碼
(2) 自定義退出狀態碼,其格式爲:
return 從函數中返回,用最後狀態命令決定返回值
return 0 無錯誤返回。
return 1-255 有錯誤返回bash
交互式環境下定義和使用函數
示例: 網絡
dir() { > ls -l > }
定義該函數後,若在$後面鍵入dir,其顯示結果同ls -l的 做用相同
dir
該dir函數將一直保留到用戶從系統退出,或執行了以下所 示的unset命令:
unset dir
在腳本中定義及使用函數
函數在使用前必須定義,所以應將函數定義放在腳本開始部分,直至 shell首次發現它後才能使用
調用函數僅使用其函數名便可app
示例:less
[root@localhost hanshu]# cat linshi #!/bin/bash #fun1 holle() #定義函數holle { echo "holle there taday's date is `date +%F`" #函數體 } echo "now going to the function hello" holle #腳本自身調用函數 echo "back from the function" [root@localhost hanshu]# .linshi #調用函數的輸出 now going to the function hello holle there taday's date is 2018-03-21 back from the function
使用函數文件
能夠將常用的函數存入函數文件,而後將函數文件載 入shell
文件名可任意選取,但最好與相關任務有某種聯繫。例如 :functions.main
一旦函數文件載入shell,就能夠在命令行或腳本中調用 函數。可使用set命令查看全部定義的函數,其輸出列 表包括已經載入shell的全部函數
若要改動函數,首先用unset命令從shell中刪除函數。改動完畢後,再從新載入此文件
建立函數文件
函數文件示例ide
cat functions.main #!/bin/bash #functions.main findit() #定義函數 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命令,函數將再也不顯示
環境函數 使子進程也可以使用
聲明:export -f function_name
查看:export -f 或 declare -xf
函數參數:
傳遞參數給函數:調用函數時,在函數名後面以空白分隔 給定參數列表便可;例如「testfunc arg1 arg2 ...」
在函數體中當中,可以使用$1, $2, ...調用這些參數;還 可使用$@, $*, $#等特殊變量
函數的返回值
return
0爲正確結果
1-255爲錯誤
unset取消函數定義,僅影響當前shell及其子Shell。
1.本地函數、局部變量(本地變量)
local 變量名。本地函數中對變量的修改,賦值只在當前函數運行期間有效。
在函數中定義局部變量的方法
local NAME=VALUE
#!/bin/bash func1 () { #定義函數func1 local name=test #定義一個本地函數 echo "func1:name=$name" local age=18 #定義一個本地函數 echo "func1:age=$age" echo $$ #查看當前進程 } #輸出 root@(5-0)app# echo $$ #查看腳本執行前進程號 3541 root@(5-0)app# func1 func1:name=test func1:age=18 3541 #腳本執行顯示的進程號
2.全局函數(本地變量)
不加local及declare -i(僅限整數數字) 默認爲全局函數,而declare -ig也爲全局函數(本地變量)。全局函數對變量的修改及賦值對當前shell均有效。但子Shell不繼承
func1 () { #定義函數 name=test declare -i age=16 echo "func1:name=$name, func1:age=$age" } #輸出 root@(5-0)app# . lins #載入函數 root@(5-0)app# func1 #調用函數 func1:name=test, func1:age=16 #函數輸出結果 root@(5-0)app# echo $name #在當前bash中調用函數定義的變量 test #變量值 root@(5-0)app# bash - #切換子bash中 [root@localhost app]# echo $name #顯示變量 #沒有值
3.環境函數(環境變量)
declare -xf 環境函數中的變量會對子shell繼承 或 export -f
例題:
使用函數編寫腳本,實現打印國際象棋棋盤,要求每一個格子由8個空格組成。
方法一: #!/bin/bash red() { #定義函數red輸出顯示樣式 echo -e "\033[41m \033[0m\c" } yel() { ##定義函數yel輸出顯示樣式 echo -e "\033[43m \033[0m\c" } redyel() { #定義函數redyel for ((i=1;i<=4;i++));do #橫向進行循環4次 #由於單個格子寬放大四倍,因此高度也要進行放大四倍 for ((j=1;j<=4;j++));do #進行判斷輸出順序 [ "$1" = "-r" ] && { yel;red; } || { red;yel; } done echo done } #每一個棋盤8x8,定義縱向格子數量 for ((line=1;line<=8;line++));do #進行奇偶判斷,來調用redyel函數 [ $[$line%2] -eq 0 ] && redyel || redyel -r # -r位置化參數 方法二: #此方法使用for循環,不作詳解 #!/bin/bash lvse (){ echo -e "\e[1;42m \e[0m\c" } huangse (){ echo -e "\e[1;43m \e[0m\c" } for i in {1..8};do for x in {1..4};do for n in {1..8};do a=`echo $[$[$i+$n]%2]` #奇偶判斷 if [ $a -eq 0 ];then huangse else lvse fi done echo done done
函數遞歸:
函數直接或間接調用自身 (棋盤題第一種方法間接調用)
注意遞歸層數
遞歸實例:
階乘是基斯頓·卡曼於 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)!
#!/bin/bash #定義函數fact # fact() { #進行判斷$!=0或等於1時輸出1 if [ $1 -eq 0 -o $1 -eq 1 ];then echo 1 else #不然,$1*$1-1,可是¥1-1不知道值,進行函數調用,得出$1-2的值, #$1-2取不出來值,在進行函數調用,得出$1-3的值;以此類推。 #最終中全部值取出來進行乘法運算,取出乘積,$1的值就取出來了。 echo $[$1*$(fact $[$1-1])] fi } fact $1
編寫函數,實現OS的版本判斷
編寫函數,實現取出當前系統eth0的IP地址
編寫函數,實現打印綠色OK和紅色FAILED
編寫函數,實現判斷是否無位置參數,如無參數,提示錯誤
#!/bin/bash #定義OS的版本判斷函數:version version () { ver=$(cat /etc/redhat-release | sed -r "s/.* ([0-9])[.].*/\1/") echo "當前版本號爲 $ver" } #定義取出當前系統eth0的IP地址的函數: ipaddr ipaddr () { ip=$(ip a s | grep ens33 | sed -n "2p"| cut -d/ -f1| awk '{print $2}') echo $ip } #定義打印綠色OK和紅色FAILED的函數:print_color print_color() { echo -e "\033[41mFAILED\033[0m" echo -e "\033[42mOK\033[0m" } #定義是否無位置參數,如無參數,提示錯誤的函數: judge_canshu judge_canshu() { if [ $# -eq 0 ];then echo "請輸入至少一個參數" else echo "共有 $# 個參數" fi
一、
在centos5或6上,編寫服務腳本/etc/init.d/routed,完成以下要求
(1) 腳本可接受參數:start, stop, restart, status
(2) 若是參數非此四者之一,提示使用格式後報錯退出
(3) 如是start:則建立/var/lock/subsys/routed, 並顯示「OK」
考慮:若是事先已經啓動過一次,則顯示routed already runing...
(4) 如是stop:則刪除/var/lock/subsys/SCRIPT_NAME, 並顯示「OK」
考慮:若是事先已然中止過了,則顯示"Faild"
(5) 如是restart,則先stop, 再start
考慮:若是原本沒有start,則在stop步驟顯示"Faild",而在start步驟顯示OK.
(6) 如是status, 則若是/var/lock/subsys/SCRIPT_NAME文件存在,則顯示「routed is running...」
若是/var/lock/subsys/SCRIPT_N AME文件不存在,則顯示「routed is stopped...」
(7)在全部模式下禁止啓動該服務,可用chkconfig 和 service命令管理
(8) 當start該腳本後,會在該主機上配置去往1.1.1.0和2.2.2.0網絡的路由去往172.18.0.1.
(9) 當stop該腳本後,會刪除(8)的兩條路由
(10) 當restart時,會先刪除路由,再加上.
#!/bin/bash # chkconfig: 2345 19 80 #應用於那幾個模式 ,並設置開關機啓動順序 # description:routed #服務名稱 if ! [ -d /var/lock/subsys ];then mkdir -p /var/lock/subsys &> /dev/null fi . /etc/init.d/functions start () { if [ -e /var/lock/subsys/routed ];then action "Staring routed: routed already runing" false else touch /var/lock/subsys/routed route add -net 1.1.1.0/24 gw 172.18.0.1 route add -net 2.2.2.0/24 gw 172.18.0.1 action "Starting routed:" true fi } stop () { if ! [ -e /var/lock/subsys/routed ];then action "Shutting down routed:routed not running" false else rm -rf /var/lock/subsys/routed route del -net 1.1.1.0/24 gw 172.18.0.1 route del -net 2.2.2.0/24 gw 172.18.0.1 action "Shutting down routed:" true fi } status () { if [ -e /var/lock/subsys/routed ];then echo "routed is running..." else echo "routed is stopped..." fi } case $1 in start) start;; stop) stop;; restart) stop start;; status) status;; *) echo "Usage: $0 {start|stop|status|restart} " esac
二、
編寫腳本/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退出
if ! [ $# -eq 2 ];then echo "error, must two integer" exit 0 elif #判斷輸入的參數是否是數字 ! [[ $1 =~ [0-9]+ ]] || ! [[ $2 =~ [0-9]+ ]];then echo "error,$1 or $2 not a integer" elif #判斷兩個參數數值是否相等 [ $1 -eq $2 ];then echo "$1 and $2 are equal" elif #兩個參數比較大小 [ $1 -lt $2 ];then a=$2 echo "$a is larger" else a=$1 echo "$a is larger" fi
3.
斐波那契數列又稱黃金分割數列,因數學家列昂納多·斐波那契以兔子繁殖爲例子而引入,故又稱爲「兔子數列」,指的是這樣一個數列:0、一、一、二、三、五、八、1三、2一、3四、……,斐波納契數列以以下被以遞歸的方法定義:F(0)=0,F(1)=1,F(n)=F(n-1)+F(n-2)(n≥2)
利用函數,求n階斐波那契數列
#!/bin/bash shulie () { #定義函數shulie if [ $1 -eq 1 ];then #判斷輸入數是否爲1,成立時輸出0 echo -n "0 " elif [ $1 -eq 2 ];then #判斷輸入數是否爲2,成立時輸出1 echo -n "1 " elif [ $1 -gt 2 ];then #判斷輸入數是否大於2,成立時,輸出n-1和n-2的和 #當n=3,先進行函數調用,計算出n-1和n-2的結果在進行加法運算,也就是先計算2和1的結果再相加 #當=8時,調用函數計算,7和6的值,7和6沒有給與值,又會再調用函數,可計算組成7的五、6和組成6的四、5, #可是四、五、6的也沒有,因此他們也會再次調用函數計算出組成他們各自的n-1和n-2,以此類推,最後都計算出 #來了,纔會推出咱們所要的n-1和n-2的值,想加就是咱們要的n的值 echo -n "$[$(shulie $[$1-1])+$(shulie $[$1-2])] " fi } a=$1 #作循環就能夠將從零到n的各數列值分別列出 for i in $(seq 1 $a);do shulie $i done echo #調用函數shulie ,並賦值第一個位置變量 #shulie $a
4.漢諾塔(又稱河內塔)問題是源於印度一個古老傳說。大梵天創造世界的時候作了三根金剛石柱子,在一根柱子上從下往上按照大小順序摞着64片黃金圓盤。大梵天命令婆羅門把圓盤從下面開始按大小順序從新擺放在另外一根柱子上。而且規定,在小圓盤上不能放大圓盤,在三根柱子之間一次只能移動一個圓盤,利用函數,實現N片盤的漢諾塔的移動步驟
#!/bin/bash count=0 n_1() { let count++ echo "第${count}步:將${2}號圓盤從${1}移動到${3}" } n_2() { if [ $1 -eq 1 ];then n_1 $2 1 $4 else n_2 $[$1-1] $2 $4 $3 n_1 $2 $1 $4 n_2 $[$1-1] $3 $2 $4 fi } read -p "please input the number: " num n_2 $num X Y Z num=3 n_2 3 X Y Z n_2 2 X Z Y n_2 1 X Y Z n_1 X 1 Z 輸出:第1步:將1由X移到Z n_1 X 2 Y 輸出:第2步:將2由X移到Y n_2 1 Z X Y n_1 Z 1 Y 輸出:第3步:將1由Z移到Y n_1 X 3 Z 輸出:第4步:將3由X移到Z n_2 2 Y X Z n_2 1 Y Z X n_1 Y 1 X 輸出:第5步:將1由Y移動X n_1 Y 2 Z 輸出:第6步:將2由Y移到Z n_2 1 X Y Z n_1 X 1 Z 輸出:第7步:將1由X移到Z