和其他編程語言一樣,Shell語言中也存在函數,通過函數可已將實現某一任務的命令進行封裝,可以提高程序的可讀性和重用性。
一、函數
1.什麼是函數
函數就是將一組功能相對獨立的代碼集中起來形成一個代碼塊,這個代碼塊可以實現某個具體的功能。
2.函數的定義
在Shell中有兩種定義函數的方法:
function_name ()
{
statement1
statement2
......
}
或者
function function_name ()
{
statement1
statement2
......
}
Shell中函數的命名規則和變量的命名規則基本相同,可以使用任意數字、字母或者下劃線,但是隻能以字母或者下劃線開頭。另外用戶應該使用具有意義的單詞定義函數名,提高代碼的可讀性。
注意:函數必須在調用之前定義。
#!/usr/bin/env bash
#定義函數
func()
{
echo "Hello World!"
}
#調用函數
func
Output:
$ sh test.sh
Hello World!
3.函數的調用
函數被定義後,就可以調用函數了,調用函數的語法如下:
function_name param1 param2 ......
function_name爲調用的函數名稱,後面是傳入的參數。
#!/usr/bin/env bash
#定義函數
func()
{
echo "Hello World! You Input Number is $@"
}
#調用函數
func 1 2 3 4
Output:
$ sh test.sh
Hello World! You Input Number is 1 2 3 4
注意:調用函數的時候不需要圓括號。
4.函數的鏈接
函數鏈接是指在一個函數中調用另外一個函數的過程。
#!/usr/bin/env bash
#定義函數
func()
{
echo "Hello World! You Input Number is $@"
}
main()
{
func 1 2 3 4
echo "I am main function"
}
#調用函數
main
Output:
$ sh test.sh
Hello World! You Input Number is 1 2 3 4
I am main function
注意:調用函數的時候必須滿足先定義再調用的順序。
5.函數的返回值
再Shell中,函數的返回值爲return語句來返回某個數值,這個數值的取值範圍爲0~255。這個return與exit命令返回命令執行的狀態碼類似,可以通過$?獲取函數的返回值。
#!/usr/bin/env bash
#定義函數
length()
{
echo "Hello World"
return 100
}
length
echo "$?"
Output:
$ sh test.sh
Hello World
100
除了用return返回值外,用戶可以將需要返回的數據寫入到標準輸出,然後調用程序中的函數的執行結果賦值給一個變量,這樣也可以實現函數返回值的傳遞。如下,通過echo傳遞函數返回值:
#!/usr/bin/env bash
#定義函數
length()
{
str=$1
result=0
if [ "$str" != "" ]; then
result=${#str}
fi
echo "$result"
}
len=$(length "hahaha123")
echo "the strings's length is $len"
Output:
$ sh test.sh
the strings's length is 9
6.函數和別名
在Shell中用戶可以使用alias命令來設置命令的別名,語法如下:
alias name="command"
其中name是別名,command是要取別名的命令。
函數和別名的類似之處在於,他們都是通過一個名稱的映射到一個或者一組命令,無論是函數還是別名,在調用的時候都是執行一個或者一組相關的命令。
7.再議全局變量和局部變量
除了與函數參數相關聯的特殊變量外,其他所有變量都是全局有效的。另外函數內部除了使用local關鍵字修飾的變量,其他變量也是全局變量。
二、函數的參數
1.含有參數的函數調用方法
這個在前面已經用過了,基本語法如下:
function_name arg1 arg2 ....
注意:函數調用的參數用空格隔開。
2.獲取函數參數的個數
用戶可以通過使用$#來獲取函數的參數個數。
#!/usr/bin/env bash
#定義函數
func()
{
echo "Hello World! You Input Number Count is $#"
echo "$@"
}
func 1 2 3 4
Output:
$ sh test.sh
Hello World! You Input Number Count is 4
1 2 3 4
3.通過指定變量接收參數值
與Shell腳本一樣,用戶可以在函數中通過$後面接數字的方式獲取參數值,需要注意的是$0獲取的是腳本的名稱。
#!/usr/bin/env bash
#定義函數
func()
{
echo "$1 $2 $3 $4"
}
func 1 2 3 4
Output:
$ sh test.sh
1 2 3 4
4.移動位置參數
在Shell腳本中用戶可以通過shift命令來使所有參數向左移動一個位置,從而使用戶可以使用9以內的位置變量獲取超過9個的參數:
#!/usr/bin/env bash
#定義函數
func()
{
while (($# > 0))
do
#只是用$1就可以讀取所有變量
echo -n "$1 "
shift
done
}
func 1 2 3 4 5 6 7 8 9 10
Output:
$ sh test.sh
1 2 3 4 5 6 7 8 9 10
注意:shift命令會影響$#的值。
5.通過getopts接收函數參數
getopts是bash內置的一個命令,通過命令,用戶可以獲取函數的選項以及參數值,或者是腳本的命令行選項以及參數值,語法如下: getopts optstring
在上面的語法中,參數optstring包含一個可以爲getopts識別的選項名稱列表。如果某個選項名稱後面有一個冒號,則表示可以爲該選項提供參數值。同時參數值被保存在$OPTARG的系統變量中。getopts命令會遍歷每個選項,選項名稱被保存在args變量中,這個args可以自己起名字,接在optstring後面即可,下面舉個例子:
#!/usr/bin/env bash
function Usage()
{
cat << EOF
test [-h] [-f <filename>] [-v <version>] [-d <descfile>]
EOF
}
function GetOpts()
{
while getopts "hf:v:d:" arg #參數只能由一個字母組成,參數後面接:,表示該參數後面需要接入參數
do
case "$arg" in
h)
Usage
;;
f)
echo "filename: ${OPTARG}"
;;
v)
echo "version: ${OPTARG}"
;;
d)
echo "descfile: ${OPTARG}"
;;
?) #當選項不匹配時,varname的值被設置爲?
exit 1
;;
esac
done
}
GetOpts -f test -v v1.0 -d test2
#GetOpts -h
Output:
函數後面接-h
random@random-wz MINGW64 /d/GOCODE/api-test
$ ./test.sh
test [-h] [-f <filename>] [-v <version>] [-d <descfile>]
函數後面接-f test -v v1.0 -d test2
random@random-wz MINGW64 /d/GOCODE/api-test
$ ./test.sh
filename: test
version: v1.0
descfile: test2
6.間接參數傳遞
在shell中也支持間接參數傳遞。所謂間接參數傳遞是指通過間接變量引用,來實現函數參數的傳遞,如果一個變量的值是另外一個變量的變量名,則該變量稱爲間接變量:
example:
#定義兩個變量,var的值是name變量的名稱
var=name
name=random
則用戶可以通過下面兩種方式來調用name變量:
${name}
${!var}
7.通過全局變量傳遞數據
參數的作用是在主程序和函數之間傳遞數據。但是,用戶除了可以使用參數傳遞數據之外,還可以通過全局變量來傳遞。但是全局變量的作用域是整個程序,包括函數內部。儘管這種方式是有效的,但是在許多編程語言中,這種方式是不被推薦的,因爲它會導致程序結果不清晰,代碼可讀性變差。
8.傳遞數組參數
在某些情況下,用戶可以將一個數組作爲參數傳遞給某個函數,然後函數對數組內容進行相應的處理。但嚴格來說,Shell並不支持將數組作爲參數傳遞給函數,但用戶可以通過一些變通的方式實現數組參數的傳遞。
首先,用戶可以將數組的元素展開,以空格隔開,然後作爲多個由空格隔開的參數傳遞給函數。
#! /usr/bin/env bash
#定義函數
function func()
{
echo "number od elements is $#."
while [ $# -gt 0 ]
do
echo "$1"
shift
done
}
#定義數組
a=(a b "c d" e)
#注意這裏用雙引號將數組括起來,如果不括起來,則數組中的"c d"將被認爲是兩個元素,因爲a[@]的值爲a b c d e
func "${a[@]}"
Output:
$ sh test.sh
number od elements is 4.
a
b
c d
e
三、庫函數文件
1.函數庫文件的定義
創建一個函數庫文件的過程類似於編寫一個Shell腳本。腳本和庫文件唯一的區別是庫文件中通常只包含函數,而腳本中則既可以還包含函數也可以包含執行的代碼。由於函數庫文件是由主程序載入並執行,因此用戶無需擁有庫文件的執行權限,只要擁有讀取權限即可。
2. 函數庫文件的調用
當庫文件定義好之後,用戶可以通過下面的方式載入庫文件:
. filename
其中filename爲庫文件的名稱,圓點和庫文件名稱之間有一個空格。
舉個例子,我們先創建一個庫文件test1.sh,內容如下:
#!/usr/bin/env bash
#輸出 Hello World
function HelloWorld()
{
echo "Hello World"
}
#計算兩個數的和
function Sum()
{
echo $(($1 + $2))
}
在腳本中載入庫文件:
#!/usr/bin/env bash
. ./test1.sh
HelloWorld
Sum 1 2
Output:
$ sh test.sh
Hello World
3
四、遞歸函數
Shell中也支持函數的遞歸調用,用法和其他編程語言一樣,遞歸函數通過反覆的調用本身,直到滿足某一個條件退出。這裏舉一個leetcode上面的題目:
我們通過shell來解答這道題目:
注意:這裏還沒有想到不使用if語句如何解答本題目,如果你有好的想法,歡迎評論區交流
#!/usr/bin/env bash
RET=1
function sumNums()
{
#定義兩個變量,n爲輸入的值,m爲n-1
local n=$1
local m=$(($n - 1))
#如果m小於1則停止遞歸
if [[ ${m} -lt 1 ]]
then
#當遞歸函數接收到的參數爲1則返回1
RET=1
else
#調用遞歸函數,傳遞參數m ,其中m=n-1
sumNums ${m}
#RET爲記錄函數返回值的參數,他等於遞歸函數的返回值加遞歸函數接收的參數
# 比如n=2, 則m=1, RET=2 + (sumNums 1),sumNums 1返回1,所以當n=2時,函數返回2+1=3
RET=$((n + $RET))
fi
}
sumNums $1
echo ${RET}