bash腳本之函數簡單介紹、應用及函數的簡單遞歸調用
在bash中,函數是由命令和語句結構構成的能夠實現特定功能的集合;
為什麽要在bash中引入函數?
在bash腳本編寫過程中有可能會出現重復且不做任何改變的代碼內容,如果這類內容全靠原始代碼書寫的話不易於排錯和優化;因此我們可以選擇將此類代碼封裝在函數中,在適當的場景中可以重復調用執行;
像此類被封裝起來的代碼塊通常稱其為模塊,也叫函數;
註意:
1.想要使用函數,必須在使用前先定義出來;
2.如果在某個bash腳本中包含了函數體,默認函數體中的各命令和語句不會執行的;只有在調用函數名的時候,才會執行函數體中的命令和語句;
3.通常需要重復執行的代碼塊或命令集,可以封裝成函數;
4.被調用的函數只能在調用的函數的shell中被執行;
定義函數的方法:
函數有兩部分組成:
函數名+函數體
函數名:調用函數時所使用的字符串標識;在一個執行環境中,函數名是不允許重復定義的;
函數體:能夠實現特定獨立功能的shell命令或結構化語句塊;
定義的語法結構:
語法一:
function func_name {
func_body
}
語法二:
func_name() {
func_body
}
註意:在語法二的格式中,func_name和()之間絕對不能有空白字符存在;
註意:函數可以在腳本中被定義,也可以在當前shell中通過交互式環境定義;
函數的使用方法:
函數在定義的時候,其函數體中包含的所有命令或結構化語句都不會被執行;只有在函數被調用時,才能執行其函數體中的各命令和語句;
調用方式:在命令行腳本中,通過直接給出的函數名的方式進行函數調用;
通常可以將常用的函數存放在專門用於保存函數的文件中,如果想要調用這個文件中已經被定義保存的函數,只需要在命令行或腳本中,使用source命令(.命令)加載文件內容到當前shell中,然後在直接使用函數名調用函數即可;
函數的撤銷:
unset命令
格式:# unset func_name
註意:可以使用set命令查看當前已經定義並生效的函數;
函數的返回值:
兩種返回值:
函數的執行結果返回值:
1.在函數體中所添加的命令有標準輸出;
2.在函數體中使用echo或printf命令強制輸出返回信息;
函數的執行狀態返回值:
1.默認情況下其狀態返回值為函數體中最後一條命令的狀態返回值;
2.自定義狀態返回值或者叫做退出碼;
return命令:
return: return [n]
從一個 shell 函數返回
n:0-255(1,2,127為系統保留的狀態碼,盡量不用)
0:表示無錯誤返回
1-255:表示有錯誤返回;
註意:在函數被調用執行時,一旦遇到return命令則不會再繼續執行函數體中其他的後續命令,立刻結束此次函數的調用執行;
函數的生命周期:
一般來講,從函數被調用時開始,知道函數體中所有的命令和結構化語句全部執行完成或遇到return命令,函數的調用結束;
函數的實參:
對應的bash函數來說,沒有形參,只有實參;
bash函數的實參是使用$1,$2...位置變量來提供數據的;
func_name pos1 pos2 ...
可以使用$@或者$*表示全部的參數列表;
可以使用$#計算參數的個數;
註意:為函數提供參數時使用的位置變量,是調用函數名時在函數名後面的對用位置上的參數信息;與腳本的位置參數不是一回事;
變量:
函數被調用時,必須在某特定的shell中被調用,因此,函數中可以繼承並識別出環境變量和由調用函數shell定義的本地變量;
在函數中還可以定義局部變量;而局部變量僅在函數的生命周期內有效;在結束函數執行之前,應該撤銷所有該函數定義的局部變量;
局部變量的定義方法:
local VAR_NAME=VALUE
變量的替換方式:
前提:定義環境變量:
export MYVAR=qhdlink
示例:
#!/bin/bash
#區別全局變量、本地變量、局部變量;
testvar() {
#local命令定義局部變量,只在局部有效;
local MYVAR=chinalink
echo "Internal function: $MYVAR"
}
#全局變量
echo "Global variables: $MYVAR"
MYVAR=link
#本地變量
echo "External function, $MYVAR"
#調用局部變量
testvar
函數的遞歸調用:
廣義:在一個函數中調用另一個函數
狹義:在函數體中調用函數自身;
直接調用:
func1(){
func1
}
間接調用:
func2(){
func1
}
func1(){
func2
}
函數的直接遞歸調用示例1:
計算某個數字的階乘:
利用for循環:
#!/bin/bash
#本腳本計算某個數字的階乘:
#定義fact為1;
fact=1
#如果給定的數字等於0,就輸出0的階乘為$fact為1,如果數字等於1就輸出1的階乘為$fact為1,否則就開始計算輸入數字的階乘;
if [ $1 -eq 0 ] ; then
echo "0! is $fact"
elif [ $1 -eq 1 ] ; then
echo "1! is $fact"
else
#利用for循環計算階乘,從1開始一直到$1;
for I in $(seq $1) ; do
#階乘是n!=1×2×3×...×n,所以$1的階乘是$1!=1×2×3×...×$1;
let fact=$[fact*$I]
done
echo "${1}! is $fact"
fi
#回收變量;
unset fact I
利用函數遞歸調用:
#!/bin/bash
#本腳本利用函數計算某個數字的階乘:
#定義函數fact;
fact(){
#如果給定的數字等於0或1就執行顯示數字的階乘等於1,否則就利用函數計算給定數字為非0非1的階乘;
if [ $1 -eq 0 ] || [ $1 -eq 1 ] ; then
echo 1
else
#用$1減去$($1-1)的階乘,n的階乘就是nx(n-1)!的階乘,n-1的階乘就是(n-1)x(n-2)!的階乘,依次類推;
echo "$[$1*$(fact $[$1-1])]"
fi
}
echo "${1}! is $(fact $1)"
函數的直接遞歸調用示例2:
斐波那波數列(黃金分割數列)
1 1 2 3 5 8 13 21 34 55 ...
#!/bin/bash
#本腳本顯示斐波那波數列(黃金分割數列);
#定義函數fabonacci;
fabonacci(){
#如果給定的數字為1或者2就顯示數列1,否則就利用函數計算給定數字為非1非2的黃金分割數;
if [ $1 -eq 1 ] || [ $1 -eq 2 ] ; then
echo -n "1 "
else
#函數的式子來自黃金分割數列的規律,利用函數的自我調用來計算顯示黃金分割數,給定數字的黃金分割數列規律是:給定數字等於給定數字在數列中的前兩個數字之和即如下規律;
echo -n "$[$(fabonacci $[$1-1])+$(fabonacci $[$1-2])] "
fi
}
for I in $(seq $1) ; do
fabonacci $I
done
echo
函數的直接遞歸調用示例3:
漢諾塔
#!/bin/bash
#本腳本顯示給出漢諾塔層數的搬運次數;
#定義步數step為0;
step=0
#定義函數move為挪動盤子的方法:這裏簡化利用最簡單的只有一個盤子的時候,先把盤子挪動到第二個柱子,再把盤子從第二個柱子挪動到第三個盤子;
move(){
#隨著盤子的挪動,步數也要進行增長;
let step++
#先把盤子挪動到第二個柱子,再把盤子從第二個柱子挪動到第三個盤子;
echo "$step: move disk $1 $2 --> $3"
}
#定義函數hanoi為移動規律;
hanoi(){
#當塔為1層時,直接先把盤子挪動到第二個柱子,再把盤子從第二個柱子挪動到第三個盤子,否則利用漢諾塔規律和函數的自我調用實現多層數的步數計算;
if [ $1 -eq 1 ] ; then
move $1 $2 $4
else
#除了塔底層最後一個大盤子,將其他的盤子整合到一起由第一個柱子整體挪動到第二個柱子,再將底層的最後一個大盤子挪到第三個柱子,再將第二個柱子上的其他盤子整合挪到第三個柱子:用hanoi挪到兩次,再用move挪一次,再使用調用hanoi函數完成最後的把其他盤子整體由第二個柱子移動到第三個柱子;
hanoi "$[$1-1]" $2 $4 $3
move $1 $2 $4
hanoi "$[$1-1]" $3 $2 $4
fi
}
#傳遞四個參數:層數,A柱,B柱,C柱;
hanoi $1 A B C
#回收變量
unset step A B C
bash腳本之函數簡單介紹、應用及函數的簡單遞歸調用