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

函數,什麼是函數?docker

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

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

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

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



函數的語法格式
模塊化

    格式1:函數

    function function_name() {測試

       ... 函數體this

    }spa

    格式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!

函數調用成功。

這時候有個疑惑:上面的腳本,若是咱們將調用函數操做放在定義函數的前面會發生怎樣的狀況呢?

兩種狀況:一、調用失敗二、正常調用

下面經過實驗來測試:

#!/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+x Linux信號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版)

相關文章
相關標籤/搜索