Shell編程從入門到精通——函數

和其他編程語言一樣,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}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章