技術交流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 }
- 方法1:采用關鍵字function,后面跟自定義的函數(shù)名。形式如下所示:
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