shell腳本編程之函數(shù)

技術交流QQ群:1027579432,歡迎你的加入!

歡迎關注我的微信公眾號:CurryCoder的程序人生

1.基本的腳本函數(shù)

  • 腳本函數(shù)出現(xiàn)的目的:為了解決大型處理過程中,需要將相同的重復代碼封裝起來,提高代碼的復用性。
  • 函數(shù)是一個腳本代碼塊,你可以為其命名并在代碼的任何位置重用。要在腳本中使用該代碼塊時,只要使用所起的函數(shù)名即可(即調用函數(shù))。

2.創(chuàng)建函數(shù)

  • 有兩種方法可以在bash shell腳本中創(chuàng)建函數(shù)。
    • 方法1:采用關鍵字function,后面跟自定義的函數(shù)名。形式如下所示:
      • 腳本中定義的每個函數(shù)都必須有唯一一個函數(shù)名;
      • commands是構成函數(shù)的一條或多條shell命令;
      • 調用函數(shù)時,bash shell會按命令在函數(shù)中出現(xiàn)的先后順序依次執(zhí)行;
      function 函數(shù)名 {
          commands
      }
      
    • 方法2:更接近于其他編程語言中定義的函數(shù)形式,具體格式如下所示:
      • 函數(shù)名后的空括號表示正在定義的是一個函數(shù);
      • 方法2這種格式的命令規(guī)則和之前定義shell腳本函數(shù)的格式一樣。
      name() {
          commands
      }
      

3.使用函數(shù)

  • 要在腳本中使用函數(shù)時,只需要像其他shell命令一樣,在行中指定函數(shù)名即可。例如如下所示:
    #!/bin/bash
    
    function func1 {
        echo "This is an example of a function"
    }
    
    count=1
    
    while [ $count -le 5 ]
    do
        # 下面一句是調用函數(shù)func1
        func1
        count=$[ $count + 1 ]
    done
    
    echo "This is the end of the loop"
    func1
    echo "Now this is the end of the script"
    
    # 結果
    [njust@njust tutorials]$ ./func1 
    This is an example of a function
    This is an example of a function
    This is an example of a function
    This is an example of a function
    This is an example of a function
    This is the end of the loop
    This is an example of a function
    
  • 不能在函數(shù)定義前,調用該函數(shù)。否則會出現(xiàn)像func2這樣的錯誤:
    #!/bin/bash
    
    count=1
    echo "This line comes before the function definition"
    
    function func1 {
        echo "This is an example of a function1"
    }
    
    while [ $count -le 5 ]
    do
        func1
        count=[ $count + 1 ]
    done
    
    echo "This is the end of the loop"
    
    
    func2
    echo "Now this is the end of the script"
    
    function func2 {
        echo "This is an example of a function2"
    }
    
    # 結果
    [njust@njust tutorials]$ ./func2
    This line comes before the function definition
    This is an example of a function1
    ./func2:行13: 1: 未找到命令
    
  • 注意:函數(shù)名必須唯一,否則后面定義的同名函數(shù)會覆蓋原先定義的函數(shù),如下所示。首先定義一個函數(shù)func1,然后執(zhí)行func1;接著定義一個同名的函數(shù)func1,在調用func1。此時調用的func1是第二次定義的func1,它會將第一次定義的func1函數(shù)覆蓋。
    #!/bin/bash
    
    
    function func1 {
        echo "This is the first definition of the function name"
    }
    
    
    func1
    
    function func1 {
        echo "This is a repeat of the same function name"
    }
    
    func1
    
    
    echo "This is the end of the script."
    
    # 結果
    [njust@njust tutorials]$ ./func3 
    This is the first definition of the function name
    This is a repeat of the same function name
    This is the end of the script.
    

4.返回值

  • bash shell會把函數(shù)當成一個小型的腳本,函數(shù)運行結束后會返回一個退出狀態(tài)碼,有三種方法來為函數(shù)生成退出狀態(tài)碼。
4.1 默認退出狀態(tài)碼
  • 默認情況下,函數(shù)的退出狀態(tài)碼是函數(shù)中最后一條命令返回的退出狀態(tài)碼。在函數(shù)執(zhí)行結束后,可以使用標準變量$?來確定函數(shù)的退出狀態(tài)碼。如下所示:
    #!/bin/bash
    
    
    func4() {
        echo "trying to display a non-existent file"
        ls -l badfilesd
    }
    
    echo "testing the function:"
    func4
    echo "The exit status is: $?"
    
    # 結果
    [njust@njust tutorials]$ ./func4
    testing the function:
    trying to display a non-existent file
    ls: 無法訪問badfilesd: 沒有那個文件或目錄
    The exit status is: 2
    
    
  • 因為上例中,函數(shù)中的最后一條指令沒有成功運行,因此函數(shù)的退出狀態(tài)碼為2。修改上面的例子如下所示:
    #!/bin/bash
    
    
    func4() {
        ls -l badfiles
        echo "trying to display a non-existent file"
    }
    
    echo "testing the function:"
    func4
    echo "The exit status is: $?"
    
    # 結果
    [njust@njust tutorials]$ ./func5
    testing the function:
    ls: 無法訪問badfiles: 沒有那個文件或目錄
    trying to display a non-existent file
    The exit status is: 0
    [njust@njust tutorials]$ cat func5
    
  • 修改過后的例子中,由于函數(shù)最后一條語句echo成功運行,于是此函數(shù)的退出狀態(tài)碼是0,盡管函數(shù)中有一條命令沒有正常運行。但是,使用函數(shù)的默認退出狀態(tài)碼是很危險的,因此有下面兩種方法解決。
4.2 使用return命令
  • bash shell使用return命令來退出函數(shù)并返回特定的退出狀態(tài)碼。return命令允許運行指定一個整數(shù)值來定義函數(shù)的退出狀態(tài)碼。如下例所示:
    #!/bin/bash
    
    function func6 {
        read -p "Enter a value: " value
        echo "doubling the value"
        return $[ $value * 2 ]
    }
    
    
    func6
    
    echo "The new value is $?"
    
    # 結果
    [njust@njust tutorials]$ ./func6
    Enter a value: 4
    doubling the value
    The new value is 8
    
  • 上例中,func6函數(shù)將$value變量中用戶輸入的值翻倍,return命令用來返回結果。注意:當使用這樣方法從函數(shù)中返回值時,要記住下面的兩條技巧
    • a.函數(shù)一結束就取返回值即$?變量會返回執(zhí)行的最后一條命令的退出狀態(tài)碼。
    • b.退出狀態(tài)碼必須在0~255之間即由于退出狀態(tài)碼必須小于256,因此函數(shù)的結果必須生成小于256的整數(shù)值,任何大于256的值都會產(chǎn)生一個錯誤值。
4.3 使用函數(shù)輸出
  • 上一種方法中,如果返回較大的整數(shù)值或字符串值時,就不能使用return命令返回函數(shù)的退出狀態(tài)碼了。因此,可以使用變量來保存函數(shù)的輸出。使用這種技術來獲得任何類型的函數(shù)輸出,并將其保存在變量中。如下例所示:
    #!/bin/bash
    
    
    function func7 {
        read -p "Enter a value: " value
        echo $[ $value * 2 ]
    }
    
    result=$(func7)  # 此句是重點,使用一個變量result來保存函數(shù)func7需要返回的值
    
    echo "The new value is $result."
    
    # 結果
    [njust@njust tutorials]$ ./func7
    Enter a value: 1000
    The new value is 2000.
    

5.在函數(shù)中使用變量

  • 在函數(shù)中使用變量時,需要注意它們的定義方式和處理方式,這是shell腳本中常見錯誤的根源。
5.1 向函數(shù)傳遞參數(shù)
  • 由于bash shell會將函數(shù)當作小型腳本來對待,因此可以像普通腳本那樣向函數(shù)傳遞參數(shù)。在腳本中指定函數(shù)時,必須將參數(shù)和函數(shù)放在同一行,如下所示:
    func1 $value1 10
    
  • 函數(shù)可以使用標準的參數(shù)環(huán)境變量來表示命令行上傳給函數(shù)的參數(shù),$0表示函數(shù)名稱,$1、$2等表示傳遞給函數(shù)的一般函數(shù),$#表示傳遞給函數(shù)的參數(shù)總數(shù)。在腳本中指定函數(shù)時,必須將函數(shù)和參數(shù)放在同一行,如下所示:
    #!/bin/bash
    
    function adddemo {
    if [ $# -eq 0 ] || [ $# -gt 2 ]
    then
        echo -1
    elif [ $# -eq 1 ]
    then
        echo $[ $1 + $1 ]
    else
        echo $[ $1 + $2 ]
    fi
    }
    
    echo -n "Adding 10 and 15: "
    value=$(adddemo 10 15)
    echo $value
    
    echo -n "Let's try adding just one number:"
    value=$(adddemo 10)
    echo $value
    
    echo -n "Finally,try adding three numbers:"
    value=$(adddemo 10 15 20)
    echo $value
    
    # 結果
    [njust@njust tutorials]$ ./func8
    Adding 10 and 15: 25
    Let's try adding just one number:20
    Finally,try adding three numbers:-1
    
  • 由于上述函數(shù)使用特殊參數(shù)環(huán)境變量作為自己的參數(shù)值,因此它無法直接獲取腳本在命令行中的參數(shù)值,如下所示:
    #!/bin/bash
    
    function badfunc {
    echo $[ $1 * $2 ]
    }
    
    if [ $# -eq 2 ]
    then
        value=$(badfunc)
        echo "The result is $value"
    else
        echo "Usage:badtest a b"
    fi
    
    # 運行失敗的結果
    [njust@njust tutorials]$ ./func9
    Usage:badtest a b
    [njust@njust tutorials]$ ./func9 10 14
    ./func9:行4: *  : 語法錯誤: 期待操作數(shù) (錯誤符號是 "*  ")
    The result is 
    
  • 盡管函數(shù)badfunc使用了$1和$2變量,但是它們和腳本主體中的$1和$2并不相同。要在函數(shù)中使用這些值,必須在調用函數(shù)時手動將它們傳過去。如下所示:
    #!/bin/bash
    
    function badfunc {
    echo $[ $1 * $2 ]
    }
    
    if [ $# -eq 2 ]
    then
        value=$(badfunc $1 $2)   # 調用函數(shù)時,需要將參數(shù)手動傳過去!
        echo "The result is $value"
    else
        echo "Usage:badtest a b"
    fi
    
    # 結果
    [njust@njust tutorials]$ ./func10
    Usage:badtest a b
    [njust@njust tutorials]$ ./func10 23 9
    The result is 207
    
5.2 在函數(shù)中處理變量
  • 函數(shù)定義的變量與普通變量的作用域不同,即對腳本的其他部分而言,它們是隱藏的。函數(shù)使用兩種類型的變量:
    • 全局變量
    • 局部變量
  • 全局變量:全局變量是在shell整個腳本中任何地方都有效的變量。默認情況下,在腳本中定義的任何變量都是全局變量。在函數(shù)外定義的變量可以在函數(shù)內部正常訪問。如下所示:
    #!/bin/bash
    
    
    function global_demo {
        value=$[ $value * 2 ]
    }
    
    read -p "Enter a value:" value  # 手動輸入全局變量value!
    
    global_demo
    echo "The new value is: $value"
    
    # 結果
    [njust@njust tutorials]$ ./func11
    Enter a value:12
    The new value is: 24
    
  • 局部變量函數(shù)內部使用的任何變量都可以被聲明成局部變量,只需要在變量聲明前加上local關鍵字即可。也可以在變量賦值語句中使用local關鍵字,如下所示:
    local temp=$[ $value + 5 ]
    
  • local關鍵字保證了變量只局限在該函數(shù)中。如果腳本中在該函數(shù)之外有同樣的名字變量,那么shell會保持這兩個變量的值是分離的。如下所示:
    #!/bin/bash
    
    
    function func12 {
        local temp=$[ $value + 5 ]  # 此處的temp是局部變量
        result=$[ $temp * 2 ]
    }
    
    
    temp=4  # 此處的temp是全局變量
    value=6
    
    
    func12
    
    
    echo "The result is $result"
    
    if [ $temp -gt $value ]  # 此處的temp是全局變量
    then
    echo "temp is larger"
    else
    echo "temp is smaller"
    fi
    
    # 結果
    [njust@njust tutorials]$ ./func12
    The result is 22
    temp is smaller
    

6.數(shù)組變量和函數(shù)

6.1 向函數(shù)傳數(shù)組參數(shù)
  • 將數(shù)組變量當作單個參數(shù)傳遞的話,它會不起作用。如果將數(shù)組變量myarray作為函數(shù)參數(shù),函數(shù)只會取出數(shù)組變量的第一個值1,如下所示:
    #!/bin/bash
    
    
    function func13 {
        echo "The parameters are: $@"
        thisarray=$1
        echo "The received array is ${thisarray[*]}"
    }
    
    myarray=(1 2 3 4 5)
    echo "The original array is: ${myarray[*]}"
    
    
    func13 $myarray
    
    # 結果
    [njust@njust tutorials]$ ./func13
    The original array is: 1 2 3 4 5
    The parameters are: 1
    The received array is 1
    
  • 要解決上述問題,必須將該數(shù)組變量的值分解成單個值,然后將這些值作為函數(shù)參數(shù)使用。在函數(shù)內部,可以將所有的參數(shù)重新組合成一個變量。如下所示:
    #!/bin/bash
    
    
    function func13 {
        local newarray
        newarray=($(echo "$@"))
        echo "The new array value is: ${newarray[*]}"
    }
    
    myarray=(1 2 3 4 5)
    echo "The original array is: ${myarray[*]}"
    
    
    func13 ${myarray[*]}
    
    # 結果
    [njust@njust tutorials]$ ./func14
    The original array is: 1 2 3 4 5
    The new array value is: 1 2 3 4 5
    
  • 上述改進后的方法中,用$myarray變量來保存所有的數(shù)組元素,然后將它們都放在函數(shù)的命令行上。該函數(shù)隨后從命令行中重建數(shù)組變量newarray。在函數(shù)內部,數(shù)組仍然可以像其他數(shù)組一樣使用。如下所示:
    #!/bin/bash
    
    
    function addarray {
        local sum=0
        local newarray
        newarray=($(echo "$@"))
        for value in ${newarray[*]}
        do
            sum=$[ $sum + $value ]
        done
        echo "The finally result is: $sum"
    }
    
    myarray=(1 2 3 4 5)
    echo "The original array is: ${myarray[*]}"
    arg1=$(echo ${myarray[*]})
    
    result=$(addarray $arg1)
    echo $result
    
    # 結果
    [njust@njust tutorials]$ ./func15
    The original array is: 1 2 3 4 5
    The finally result is: 15
    
6.2 從函數(shù)返回數(shù)組
  • 函數(shù)用echo語句來按正確順序輸出單個數(shù)組值,然后腳本再將它們重新放入一個新的數(shù)組變量中。如下所示:
    #!/bin/bash
    
    
    function array_demo {
        local original_array
        local new_array
        local element
        local i
        original_array=($(echo "$@"))
        new_array=($(echo "$@"))
        element=$[ $# - 1 ]
        for (( i = 0; i <= $element; i++ ))
        {
            new_array[$i]=$[ ${original_array[$i]} * 2 ]
        }
        
        echo ${new_array[*]}
    }
    
    
    myarray=(1 2 3 4 5)
    echo "The original array is: ${myarray[*]}"
    arg1=$(echo ${myarray[*]})
    
    result=($(array_demo $arg1))
    
    echo "The finally result is: ${result[*]}"
    
    # 結果
    [njust@njust tutorials]$ ./func16
    The original array is: 1 2 3 4 5
    The finally result is: 2 4 6 8 10
    

7.函數(shù)遞歸

  • 函數(shù)可以調用自己來得到結果,通常遞歸函數(shù)都有一個最終可以迭代到的基準值。如下例中的階乘示例程序:
    #!/bin/bash
    
    
    function fib {
        if [ $1 -eq 1 ]
        then
            echo 1
        else 
            local temp=$[ $1 - 1 ]
            local result=$(fib $temp)
            echo $[ $result * $1 ]
        fi
    }
    
    
    read -p "Enter value: " value
    result=$(fib $value)
    
    echo "The fib of $value is: $result"
    
    # 結果
    [njust@njust tutorials]$ ./func17
    Enter value: 5
    The fib of 5 is: 120
    

8.創(chuàng)建庫

  • 假設需要在多個腳本中使用同一段代碼,此時為了使用同一段相同的代碼而在每個腳本中都定義同樣的函數(shù)太麻煩了。bash shell允許你創(chuàng)建函數(shù)庫文件,然后在腳本中引用該庫文件即可。該方法的第一步是創(chuàng)建一個包含腳本中所需函數(shù)的公用庫文件,如下例所示有個名為myfuncs的庫文件,它定義了三個簡單的函數(shù):
    #!/bin/bash
    
    
    function addem {
        echo $[ $1 + $2 ]
    }
    
    function multem {
        echo $[ $1 * $2 ]
    }
    
    function divem {
        if [ $2 -ne 0 ]
        then
            echo $[ $1 / $2 ]
        else
            echo -1
        fi
    }
    
  • shell函數(shù)僅在定義它的shell會話內有效,如果在shell命令行的提示符下運行myfuncs腳本,shell會創(chuàng)建一個新的shell并在其中運行這個腳本。它會為新的shell定義這三個函數(shù)。但當你運行另外一個要用到這些函數(shù)的腳本時,它們是無法使用的。如果你想像普通腳本文件那樣運行庫文件,函數(shù)并不會出現(xiàn)在腳本中。如下例所示:
    #!/bin/bash
    
    ./myfuncs
    
    result=$(addem 10 15)
    echo "The result is $result."
    
    # 錯誤結果
    [njust@njust tutorials]$ ./badlibs 
    ./badlibs:行5: addem: 未找到命令
    The result is .
    
  • 使用函數(shù)庫的關鍵在于source命令。source命令會在當前shell上下文中執(zhí)行命令,而不是創(chuàng)建一個新的shell。可以使用source命令來在shell腳本中運行庫文件腳本,這樣腳本就可以使用庫文件中的函數(shù)了。source命令有個快捷的別名,稱為點操作符。要在shell腳本中運行myfuncs庫文件,只需添加. ./myfuncs即可。下例中假設myfuncs庫文件與shell腳本位于同一目錄下。如果不是這種情況,需要使用相應路徑訪問myfuncs文件。如下例所示:
    #!/bin/bash
    
    . ./myfuncs
    
    value1=10
    value2=5
    
    
    result1=$(addem $value1 $value2)
    result2=$(multem $value1 $value2)
    result3=$(divem $value1 $value2)
    
    echo "Add result is $result1"
    echo "Multiply result is $result2"
    echo "Division result is $result3"
    
    # 結果
    [njust@njust tutorials]$ ./func18
    Add result is 15
    Multiply result is 50
    Division result is 2
    

9.在命令行上使用函數(shù)

  • 有時候也很有必要在命令行界面的提示符中直接使用這些函數(shù)。和shell腳本中將腳本函數(shù)當作命令使用一樣,在命令行界面中也可以這樣做。因為一旦在shell中定義了函數(shù),就可以在整個系統(tǒng)中使用,不用擔心腳本是不是在PATH環(huán)境變量中。重點在于如何讓shell識別這些函數(shù),有幾種方法可以實現(xiàn)。
9.1 在命令行上創(chuàng)建函數(shù)
  • 在命令行上直接定義一個函數(shù),有兩種方法。注意:如果所起的函數(shù)名與內建的shell命令或另一個命令相同,函數(shù)會覆蓋原來的命令。
    • a.采用單行方式定義函數(shù);注意:當在命令行上定義函數(shù)時,必須記得在每個命令后加一個分號,分號表示本條命令的終止。如下例所示:
    [njust@njust tutorials]$ function divem { echo $[$1 / $2 ]; }
    [njust@njust tutorials]$ divem 100 3
    33
    
    • b.采用多行方式定義函數(shù);在定義時,bash shell會使用次提示符>來提示輸入更多命令。用這種方法,不用在每天命令的末尾加一個分號,只要按下回車鍵即可。如下所示:
    [njust@njust tutorials]$ function mulem {
    > echo $[ $1 * $2 ]
    > }
    [njust@njust tutorials]$ mulem 2 5
    10
    
9.2 在.bashrc文件中定義函數(shù)
  • 在命令行中定義函數(shù)的缺點是:當退出shell時,函數(shù)就消失了。一個非常簡單的方法是將函數(shù)定義在一個特定的位置,這個位置在每次啟動一個新shell時,都會由shell重新載入,這個特定的位置即.bashrc文件。bash shell在每次啟動時都會在主目錄下查找這個文件,不管是交互式的shell,還是從現(xiàn)有shell中啟動新的shell。
  • 9.2.1 直接定義函數(shù):可以在主目錄下的.bashrc文件中定義函數(shù),許多Linux發(fā)行版中已經(jīng)在.bashrc中定義了一些東西,所以注意不要誤刪?。?!。將你寫的函數(shù)添加至文件末尾即可。如下例所示的函數(shù)addem函數(shù)會在啟動新的bash shell時生效,之后就可以在系統(tǒng)上的任意位置使用這個函數(shù)了。
    # .bashrc
    
    # Source global definitions
    if [ -f /etc/bashrc ]; then
        . /etc/bashrc
    fi
    
    # Uncomment the following line if you don't like systemctl's auto-paging feature:
    # export SYSTEMD_PAGER=
    
    # User specific aliases and functions
    
    function addem {
        echo $[ $1 + $2 ]
    }
    
  • 9.2.2 讀取函數(shù)文件:只要是在shell腳本中,都可以使用source命令(或者它的別名點操作符)將庫文件中的函數(shù)添加到.bashrc腳本中。如下例所示:
    # .bashrc
    
    # Source global definitions
    if [ -f /etc/bashrc ]; then
        . /etc/bashrc
    fi
    
    # Uncomment the following line if you don't like systemctl's auto-paging feature:
    # export SYSTEMD_PAGER=
    
    # User specific aliases and functions
    
    function addem {
        echo $[ $1 + $2 ]
    }
    
    . /home/njust/tutorials/myfuncs  # 將庫文件myfuncs中的函數(shù)添加到.bashrc中
    
  • 要確保庫文件的路徑正確,以便bash shell能找到該庫文件。下次啟動shell時,庫文件中的所有函數(shù)都可以在命令行界面下使用。如下所示:
    [njust@njust ~]$ source .bashrc
    [njust@njust ~]$ addem 5 10
    15
    

10.資料下載

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容