1. 程式人生 > >linux 腳本 之 函數篇

linux 腳本 之 函數篇

函數

1 概述

函數function是由若幹條shell命令組成的語句塊,實現代碼重用和模塊化編程

函數只能作為shell程序的一部分存在。屬於shell的一段代碼,函數不是子進程。本身是腳本進程中的一部分,

所以函數裏的變量可以傳給調用該函數的腳本使用。默認情況下,這裏函數和shell的id是平級的關系,在函數的命令和shell是在同一pid下執行命令

它與shell程序形式上是相似的,不同的是它不是一個單獨的進程,不能獨立運行,而是shell程序的一部分

函數和shell程序比較相似,區別在於:

Shell程序在子Shell中運行

而Shell函數在當前Shell中運行。因此在當前Shell中,函數可以對shell中變量進行修改

2 定義函數

函數由兩部分組成:函數名和函數體

help function

語法一:

f_name(){
...函數體...
}

語法二:

function f_name{
...函數體...
}

語法三:

function f_name(){
...函數體...
}

3 函數使用

函數的定義和使用:

可在交互式環境下定義函數

可將函數放在腳本文件中作為它的一部分

可放在只包含函數的單獨文件中

調用:函數只有被調用才會執行

調用:給定函數名

函數名出現的地方,會被自動替換為函數代碼

函數的生命周期:被調用時創建,返回時終止

4 函數返回值

函數有兩種返回值:

函數的執行結果返回值:

(1) 使用echo等命令進行輸出

(2) 函數體中調用命令的輸出結果

函數的退出狀態碼:

(1) 默認取決於函數中執行的最後一條命令的退出狀態碼

(2) 自定義退出狀態碼,其格式為:

return 從函數中返回,用最後狀態命令決定返回值

return 0 無錯誤返回。

return 1-255 有錯誤返回

交互式環境下定義和使用函數

示例:

dir() {
> ls-l
> }

定義該函數後,若在$後面鍵入dir,其顯示結果同ls-l的作用相同

dir

該dir函數將一直保留到用戶從系統退出,或執行了如下所示的unset命令:

unset dir

5 在腳本中定義及使用函數

函數在使用前必須定義,因此應將函數定義放在腳本開始部分,直至shell首次發現它後才能使用

調用函數僅使用其函數名即可

6 使用函數文件

可以將經常使用的函數存入函數文件,然後將函數文件載入shell

文件名可任意選取,但最好與相關任務有某種聯系。例如:functions.main

一旦函數文件載入shell,就可以在命令行或腳本中調用函數。可以使用set命令查看所有定義的函數,其輸出列表包括已經載入shell的所有函數

若要改動函數,首先用unset命令從shell中刪除函數。改動完畢後,再重新載入此文件

創建函數文件

函數文件示例:

cat functions.main
#!/bin/bash
#functions.main
findit()
{
if [ $# -lt 1 ] ; then
echo "Usage:findit file"
return 1
fi
find / -name $1 –print
}

7 載入函數

函數文件已創建好後,要將它載入shell

定位函數文件並載入shell的格式:

. filename 或source filename

註意:此即<點> <空格> <文件名>

這裏的文件名要帶正確路徑

示例:

上例中的函數,可使用如下命令:

. functions.main

檢查載入函數

使用set命令檢查函數是否已載入。set命令將在shell中顯示所有的載入函數

set | grep 函數名

如果有結果,表示該函數已經載入

8 執行shell函數

要執行函數,簡單地鍵入函數名即可

示例:

findit groups

/usr/bin/groups

/usr/local/backups/groups.bak

刪除shell函數

現在對函數做一些改動後,需要先刪除函數,使其對shell不可用。使用unset命令完成刪除函數

命令格式為:

unset function_name

示例:

unset findit

再鍵入set命令,函數將不再顯示

9 環境函數

使子進程也可使用

聲明:export -f function_name

查看:export -f 或declare -xf

10 函數參數

函數可以接受參數:

傳遞參數給函數:調用函數時,在函數名後面以空白分隔給定參數列表即可;例如“testfunc arg1 arg2 ...”

在函數體中當中,可使用$1, $2, ...調用這些參數;還可以使用[email protected], $*, $#等特殊變量

環境變量由父進程傳給子進程,不能從子進程傳給父進程

可以將常用的功能寫出函數塊,放到統一的文件裏,後續要調用就直接用調用該函數文件就可以直接用著函數

腳本內容如下,當腳本中函數的$1 等參數是腳本的位置變量,但是一旦在函數後面跟得變量值,就是函數的位置參數。

$0特殊,$0特殊腳本的名稱,在函數中也是所運行的腳本的名稱不是函數名

testvar () {
   while [ $# -gt 0 ];do
   echo "\$1 is func $1,\$0 is $0,\$# is $#"
   shift
   done
}
testvar $1 $2
testvar ab cd ef

11 函數變量

變量作用域:

環境變量:當前shell和子shell有效

本地變量:只在當前shell進程有效,為執行腳本會啟動專用子shell進程;因此,本地變量的作用範圍是當前shell腳本程序文件,包括腳本中的函數

局部變量:函數的生命周期;函數結束時變量被自動銷毀

註意:如果函數中有局部變量,如果其名稱同本地變量,使用局部變量

在函數中定義局部變量的方法

local NAME=VALUE


通過以下實驗來測試本地變量

通過調整邏輯與&&前面的語句的執行結果的真假,來觀察RC1或者RC2的賦值情況


通過local這個關鍵字,來查看RC1或者RC2被賦予的值是否會傳到函數外面,觀察函數本地變量的區別


pwd && local RC1=$ || local RC2=$? ?


則調用腳本的時候,函數外面的RC1為空值,因為local的變量只作用於函數內


pwd && RC1=$? || RC2=$?


則調用腳本的時候,函數外面的RC1有值,為前面語句的執行結果,本例子中為0值


腳本如下

test_local () {
#pwdsss &&  RC1=$? || RC2=$?
pwd && local RC1=$ || local  RC2=$? ?
}
test_local
echo RC1=$RC1
echo RC2=$RC2

11 函數遞歸

函數遞歸:

函數直接或間接調用自身,遞歸會將上一次運算的結果全部帶入下一次的計算

註意遞歸層數

遞歸實例:

階乘是基斯頓·卡曼於1808 年發明的運算符號,是數學術語

一個正整數的階乘(factorial)是所有小於及等於該數的正整數的積,並且有0的階乘為1,自然數n的階乘寫作n!

n!=1×2×3×...×n

階乘亦可以遞歸方式定義:0!=1,n!=(n-1)!×n

n!=n(n-1)(n-2)...1

n(n-1)! = n(n-1)(n-2)!

函數遞歸示例

示例:fact.sh

#!/bin/bash
#
fact() {
if [ $1 -eq 0 -o $1 -eq 1 ]; then
echo 1
else
echo $[$1*$(fact $[$1-1])]
fi
}
fact $1

12 fork炸彈

fork炸彈是一種惡意程序,它的內部是一個不斷在fork進程的無限循環,實質是一個簡單的遞歸程序。

由於程序是遞歸的,如果沒有任何限制,這會導致這個簡單的程序迅速耗盡系統裏面的所有資源

函數實現

:(){ :|:& };:

bomb() { bomb | bomb & }; bomb

腳本實現

cat Bomb.sh

#!/bin/bash

./$0|./$0&

例子

bomb(){
    bomb|bomb&
}
echo "$0 is danger,it will make your computer resource  exhaustedin a few seconds,your computer need restart after that"
read -p "Please input your choice,y or n: " choice
case $choice in
y)
  bomb
  ;;  
n)
  echo "You make a good chioce"
  exit 6
  ;;  
*)
  echo "Your input is wrong"
  exit 8
  ;;
esac

13 總結

以上介紹了函數的定義,調用,返回值,取消函數,變量,遞歸等方面進行了介紹。希望對使讀者再使用函數時有所幫助

本文出自 “自學linux” 博客,請務必保留此出處http://ghbsunny.blog.51cto.com/7759574/1962919

linux 腳本 之 函數篇