Linux基礎之bash腳本進階篇-函數

函數,什麼是函數?

函數的出現最初是在數學中,它的數學定義如下:在某變化過程中有兩個變量x,y,按照某個對應法則,對於給定的x,有唯一確定的值y與之對應,那麼y就叫做x的函數。

而在計算機中函數的內涵發生了一些變化。

在編程中,爲了簡化代碼量,通常會將經常調用的一些代碼模塊化,並一一個名字表示,當再次使用該模塊時只需要輸入該名字,系統會自動去讀取該名字所對應的代碼模塊。因此在計算機中把一段獨立功能的代碼當做一個整體,併爲之命一個名字,命名的代碼段即爲函數

雖然此函數非彼函數但函數最本質的意義並未改變:按照某個對應法則的對應關係



函數的語法格式

    格式1:

    function function_name() {

       ... 函數體

    }

    格式2:

    function_name () {

       ... 函數體

    }



函數的調用

注:定義函數的代碼段不會自動執行在調用時執行;調用即在代碼中給定函數並即可;函數名出現的任何位置,在代碼執行時,都會被自動替換爲函數代碼。

示例:定義一sayhello函數,輸出“Hello,World!”

#!/bin/bash
#function sayhello
#author chawan
#定義函數
function sayhello () {
echo "Hello,World!"
}
#調用函數
sayhello

運行腳本

[root@docker test]# sh 20160909-1
Hello,World!

函數調用成功。

這時候有個疑惑:上面的腳本,如果我們將調用函數操作放在定義函數的前面會發生怎樣的情況呢?

兩種情況:1、調用失敗2、正常調用

下面通過實驗來測試:

#!/bin/bash
#function sayhello
#author chawan
#調用函數
sayhello
#定義函數
function sayhello () {
echo "Hello,World!"
}

運行腳本

[root@docker test]# sh 20160909-1
20160909-1:行5: sayhello: 未找到命令

系統報錯,爲什麼報錯呢

首先,腳本的執行總體上是順序執行,因此會先執行sayhell,通過定義的環境變量$PATH定義的路徑找不到sayhello對應的命令因此報“未發現sayhello命令”。

我們在終端命令行中輸錯命令報錯也是這個原因。終端命令行默認會將最左面輸入的內容當做命令,因此若是錯誤的命令,不是命令的命令等內容都會報錯。

通過上面的對比,我們至少知道函數的調用若是在同一個腳本中,調用操作需要在定義的函數後面



函數的鏈接

所謂函數鏈接:是指在某個shell函數中調用另外一個函數的過程。

shell允許用戶函數的嵌套使用

示例:演示某個函數中同時調用多個其他函數。

#!/bin/bash
#函數間的調用
#author chawan date:20160909
john() {
echo "Hello,John!"
}
tom() {
john
echo "Hello,tom!"
}
sayhello() {
tom
lilei
}
lilei() {
echo "Hello,lilei!"
}
sayhello

運行腳本,結果如下:

[root@docker test]# sh 20160909-2
Hello,John!
Hello,tom!
Hello,lilei!

這個腳本我故意將lilei函數放在sayhello函數後面,結果sayhello正常調用lilei,所以只要調用函數的位置在當前腳本所設定的函數之後即不受函數順序的影響。函數之間無順序制約



函數返回值

在介紹函數返回值前先講述下跟函數返回值有關的狀態退出碼

狀態退出碼

shell中運行的每個命令都使用退出狀態碼(exit status)來告訴shell它完成了處理。退出狀態碼是一個0-255之間的整數值,在命令結束運行時由命令傳給shell。你可以捕獲這個值並在腳本中使用。

查看退出狀態碼

Linux提供了$?專屬變量來保存上個執行的命令的退出狀態碼。你必須在你要查看的命令之後馬上查看或使用$?變量。它的值會變成shell中執行的最後一條命令的退出狀態碼:

示例:查看命令狀態碼

[root@docker test]# ls /etc >> /dev/null
[root@docker test]# echo $?
0
[root@docker test]# basdc
bash: basdc: 未找到命令...
[root@docker test]# echo $?
127

退出狀態碼大體分兩種:

一種是命令正確執行的狀態碼,該狀態碼爲:0

一種是命令錯誤執行的狀態碼,爲1-255

                            Linux退出狀態碼

狀態碼描述
0命令成功結束
1通用未知錯誤
2誤用shell命令
126命令不可執行
127沒找到命令
128無效退出參數
128+xLinux信號x的嚴重錯誤
130命令通過Ctrl+C終止
255退出狀態碼越界

在腳本中可以指定退出狀態碼的值,通過命令exit實現

示例:指定退出狀態碼

#!/bin/bash
#exit status
echo "Hello,World!" && exit 400

執行腳本

[root@docker test]# sh 20160909-3
Hello,World!
[root@docker test]# echo $?
144

我指定的是400,真實的值爲144.爲什麼會是這個值?

首先退出狀態碼取值範圍爲0-255。一般在不指定退出狀態碼時,所有的狀態值都會在0-255之內,當我們手動指定退出狀態碼後,若值超過255,那麼shell會通過模(模就是256)運算得到相應的退出狀態碼400對256取模值爲144.


函數返回值(函數退出狀態碼)

bash shell會把函數當做小型腳本,運行結束時也會返回一個退出狀態碼。

默認情況下,函數的退出狀態碼是函數中最後一條命令返回的退出狀態碼。

示例:函數狀態碼演示

#!/bin/bash
#function exit status
func_test() {
  echo "Hi,this is a function test!"
  lll -a /etc
}
func_test
echo "The exit status is: $?"

運行腳本

[root@docker test]# sh 20160909-4
Hi,this is a function test!
20160909-4:行5: lll: 未找到命令
The exit status is: 127

函數的退出狀態碼是127,這是因爲函數中最後一條命令沒有成功執行,更具體點就是沒有找到lll命令。但這個退出狀態碼無法知道函數中其他命令是否成功執行。


再看一個例子

#!/bin/bash
#function exit status
func_test() {
  lll -a /etc
  echo "Hi,this is a function test!"
}
func_test
echo "The exit status is: $?"

僅僅是將先前函數體中的2個命令換個位置。執行該腳本

[root@docker test]# sh 20160909-5
20160909-4:行4: lll: 未找到命令
Hi,this is a function test!
The exit status is: 0

這次,由於函數最後一行命令正確執行,函數的退出狀態碼就是0,儘管函數中有一條命令沒有成功運行。

使用函數的默認退出狀態碼是很危險的,幸運的是return命令可以解決這個問題。

bash shell使用return命令來退出函數並返回特定的退出狀態碼。return命令允許指定一個整數值來定義函數的退出狀態碼,整數範圍爲0-255

示例:使用return指定函數退出狀態碼

#!/bin/bash
#using the return command in a fuction
func_test() {
  lll -a /etc 
  echo "Hi,this is a function test!"
  return 4
}
func_test
echo "The exit status is: $?"

還是使用相同的函數,在函數最後加上return指定的狀態碼4.

執行腳本

[root@docker test]# sh 20160909-6
20160909-4:行4: lll: 未找到命令
Hi,this is a function test!
The exit status is: 4

之所以費這麼大勁介紹函數狀態碼是因爲在今後的腳本中一個設置規範的函數返回值能幫我們簡化腳本



傳遞參數給函數

在函數體中,可以使用$1,$2,...引用傳遞給函數的參數;還可以在函數中使用$*$@引用所有參數,$#引用傳遞的參數個數;在調用函數時,在函數名後以空白符分隔給定參數列表即可。

示例:傳遞參數給函數

#!/bin/bash
#傳遞參數給函數
func_var() {
#輸出所有的參數
echo "all parameters are $*"
#輸出腳本名稱
echo "the script's name is $0"
#輸出第一個參數
echo "the first parameter is $1"
#輸出第二個參數
echo "the second parameter is $2"
}
func_var hello world

執行腳本

[root@docker test]# sh 20160909-7
all parameters are hello world
the script's name is 20160909-7
the first parameter is hello
the second parameter is world



函數的變量作用域

局部變量:作用域是函數的生命週期;在函數結束時被自動銷燬。

定義局部變量的方法:local VAR=VALUE

本地變量:作用域是運行腳本的shell進程的生命週期;因此,其作用範圍爲當前shell

爲什麼要用到局部變量?

下面舉個例子說明這個問題

#!/bin/bash
#在函數外定義本地變量
var="Hello,World"
func_1() {
#在函數內改變變量內容
var="Hi,var is changed"
}
echo "$var"
func_1
echo "$var"

執行腳本

[root@docker test]# sh 20160909-8
Hello,World
Hi,var is changed

結果顯示在調用函數後,原有的本地變量var被替換了。還好這個變量並不是重要的部分,想想若是PATH被替換了,那麼這個函數的罪過就大了。因此我們如何即調用函數中定義的變量同時又不對本地變量造成任何影響呢?局部變量的出現就是爲了解決這個問題

下面看看在使用了局部變量後的效果。

#!/bin/bash
#在函數外定義本地變量
var="Hello,World"
func_1() {
#在函數內改變變量內容
local var="Hi,var is changed"
echo "$var"
}
echo "$var"
func_1
echo "$var"

運行腳本

[root@docker test]# sh 20160909-9
Hello,World
Hi,var is changed
Hello,World

該實驗結果說明,使用局部變量後,函數體中出現的變量作用範圍只存在於當前函數生命週期。



遞歸函數

遞歸函數:即函數可以直接或間接地調用自身

在函數的遞歸調用中,函數既是調用者,又是被調用者。

遞歸函數的調用過程就是反覆地調用其自身,每調用一次就進入新的一層。

示例:使用遞歸函數表示階乘表達式

#!/bin/bash
#using recursion
func_r() {
if [ $1 -eq 1 -o $1 -eq 0 ];then
  echo "1"
else
  local temp=$[ $1 - 1 ]
  local result=`func_r $temp`
  echo $[ $result * $1 ]
fi
}
read -p "Enter a number: " value
[ -z $value ] && echo "None,please inset a number" && exit 1
result=`func_r $value`
echo "The func_r of $value is :  $result"

執行腳本

[root@docker test]# sh 20160909-11
Give a number : 3
The func_r of 3 is : 6
[root@docker test]# sh 20160909-11
Give a number : 5
The func_r of 5 is : 120

遞歸函數在新手看來是比較難理解的,如果理解起來比較困難,建議給一個小的數,通過列舉,來看該函數的執行效果,一層一層,一直到其結束。這樣會對遞歸有個更清晰的認識。



小結

本文主要介紹內容:

函數、函數的語法格式、函數的調用、函數的鏈接、給函數傳遞參數、函數返回值、函數的變量作用域、遞歸函數。



參考書籍:

shell入門到精通

Linux命令行與shell腳本編程大全(第2版)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章