函數介紹
定義:把一段獨立功能的的代碼當做一個整體,並為之一個名字,命名的代碼段,此即為函數;
功能:函數function是由若干條shell命令組成的語句塊,實現代碼重用和模塊化編程。
注意:定義函數的代碼段不會自動執行,在調用時執行;所謂函數調用,就在代碼中給定函數名稱即可;函數名出現的任何位置,在代碼執行時,都會被自動替換為函數代碼;
函數和shell程序比較相似,區別在於:
Shell程序在子Shell中運行,而函數在當前Shell中運行。因此在當前Shell中,函數可以對shell中變量進行修改;
它與shell程序形式上是相似的,不同的是它不是一個單獨的進程,不能獨立運行,而是shell程序的一部分。
函數語法和使用
語法:函數由兩部分組成函數名和函數體。
語法一:
function f_name {
...函數體...
}
語法二:
f_name() {
...函數體...
}
函數的定義和使用:
可在交互式環境下定義函數
可將函數放在腳本文件中作為它的一部分(自己定義自己用)
可放在只包含函數的單獨文件中
函數調用:函數只有被調用才會執行
調用方法:在代碼中給定函數名稱,函數名出現的地方,會被自動替換為函數代碼
函數的生命周期:被調用時創建,返回時終止
函數的兩種返回值
函數的執行結果返回值:使用echo或printf命令進行輸出;函數體中調用命令的輸出結果
函數的退出狀態碼:其狀態返回結果為函數中執行的最后一條命令的狀態結果
自定義退出狀態碼:return
return [0-255] :從函數中返回,用最后狀態命令決定返回值
0:成功,無錯誤返回
1-255:失敗,有錯誤返回
示例:給定一個用戶名,顯示其用戶名,id和默認的shell
#!/bin/bash
#
userinfo() {
if id $username &> /dev/null; then
getent passwd $username| cut -d: -f1,3,7
else
echo "No such user"
fi
}
[ $# -lt 1 ] && echo "At least one username." && exit 2
username=$1
userinfo
username=$2
userinfo
示例:以函數的方式改寫服務框架腳本
#!/bin/bash
#
#chkconfig: - 50 50
#description: test service scipt
#
prong=$(basename $0)
lockfile=/var/lock/subsys/$prong
start() {
if [ -f $lockfile ];then
echo "$prong is running yet."
else
touch $lockfile
[ $? -eq 0 ] && echo "start $prong finished."
fi
}
stop() {
if [ -f $lockfile ];then
rm -f $lockfile
[ $? -eq 0 ] && echo "stop $prong finished."
else
echo "$prong is not running."
fi
}
status() {
if [ -f $lockfile ];then
echo "$prong is running"
else
echo "$prong is stopped"
fi
}
usage() {
echo "Usage: $prong {start|stop|restart|status}"
}
case $1 in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
status)
status
;;
*)
usage
exit 1
esac
函數定義及用法詳述
函數可以定義在三個場景:交互環境下、腳本中 、單獨的函數文件
1、交互環境下定義和使用函數:
示例:鍵入函數名,注意左大括號和函數體之間需要有空格。這里要注意優先級,如果函數名和定義的別名、內部命令外部命令同名,優先級為:別名>函數>內部命令>外部命令;以該方法定義的函數將一直保留到用戶從系統退出,或執行了unset命令:
[root@centos7 function]# fun() { who; hostname ;}
[root@centos7 function]# fun
root pts/0 2017-03-02 09:39 (192.168.1.101)
centos7
2、在腳本文件中定義和使用函數
方法:函數在使用前必須定義,因此應將函數定義放在腳本開始部分,直至shell首次發現它后才能使用;調用函數僅使用其函數名即可。
注意:在腳本中的函數在外面是用不了的,因為不在同一個shell中,運行腳本是開了一個子shell,而在外面是父shell。
示例:
#!/bin/bash
#
fun1() {
echo "this is fun1"
}
echo "fun1 before"
fun1
echo "fun1 after"
# 執行結果如下:
[root@centos7 function]# bash test1.sh
fun1 before
this is fun1 # 函數只有在被調用才會執行
fun1 after
3、使用函數文件定義函數
可以將經常使用的函數存入函數文件,然后將函數文件載入shell。文件名可任意選取,但最好與相關任務有某種聯系。例如:functions.main
一旦函數文件載入(source或.)shell,就可以在命令行或腳本中調用函數。可以使用set命令查看所有定義的函數,其輸出列表包括已經載入shell的所有函數。若要改動函數,首先用unset命令從shell中刪除函數。改動完畢后,再重新載入此文件
函數文件載入shell的格式:
. /PATH/TO/SOMEFILE或者source /PATH/TO/SOMEFILE
查看是否載入:
用source或 . 載入shell后,可使用set命令檢查函數是否已載入。set命令將在shell中顯示所有的載入函數
刪除shell函數:
現在對函數做一些改動。首先刪除函數,使其對shell不可用。使用unset命令完成此功能。
命令格式為:unset function_name
在腳本中載入函數:在腳本中使用函數,只要source FILES或者. FILE(存放函數的文件),就可以載入到當前shell當中,調用函數了。
注意:
1、Shell函數在當前Shell中運行。因此在當前Shell中,函數可以對shell中變量進行修改;
2、當函數載入到當前環境中或腳本中時,當前環境或者腳本中定義的變量會和函數中定義的變量起沖突,這是因為,我們在當前環境中或者腳本中定義的為本地變量,函數載入當前shell之后,其實就是當前shell的一部分,所以定義的本地變量對其也有效。為了避免這種沖突,函數要使用局部變量。
示例:
1、首先創建一個函數文件,此函數文件中可以存放多個函數;
[root@centos7 function]# cat funs
#!/bin/bash
#
fun1() {
echo "This is fun1"
}
fun2() {
echo "This is fun2"
}
2、在當前shell中要想調用此函數,要使用 source 或者 . 加載到當前shell 中方可
# 直接調用,bash(相當於開了一個子shell)都不行
[root@centos7 ~]# fun1
-bash: fun1: 未找到命令
[root@centos7 ~]# bash fun1 # 相當於
bash: fun1: 沒有那個文件或目錄
# source 加載到當前shell
[root@centos7 ~]# source /root/bin/function/funs
[root@centos7 ~]# fun1
This is fun1
[root@centos7 ~]# fun2
This is fun2
# 刪除shell函數,再執行就沒有了
[root@centos7 ~]# unset fun1
[root@centos7 ~]# fun1
-bash: fun1: 未找到命令
[root@centos7 ~]# fun2
This is fun2
[root@centos7 ~]# unset fun2
[root@centos7 ~]# fun2
-bash: fun2: 未找到命令
3、在腳本中使用函數,只要source FILES或者. FILE(存放函數的文件),就可以載入到當前shell當中,調用函數了。
[root@centos7 function]# cat test2.sh
#!/bin/bash
#
source /root/bin/function/funs
fun1
echo "taotao"
fun2
# 執行結果如下:
[root@centos7 function]# fun1
-bash: fun1: 未找到命令 # 在當前shell中不能使用,因為腳本為子shell,當前環境為父shell
[root@centos7 function]# fun2
-bash: fun2: 未找到命令
[root@centos7 function]# bash test2.sh
This is fun1
taotao # 調用函數成功
This is fun2
函數參數:
函數可以接受參數:
傳遞參數給函數:調用函數時,在函數名后面以空白分隔給定參數列表即可;例如"testfunc arg1 arg2 ..."
在函數體中當中,可使用$1, $2, ...調用這些參數;還可以使用$@, $*, $#等特殊變量
示例1:
1、添加10個用戶,(添加用戶的功能使用函數實現),用戶名作為參數傳遞給函數
[root@centos7 function]# cat useradd.sh
#!/bin/bash
#
addusers () {
if id $1 &> /dev/null; then
return 5
else
useradd $1
retval=$? # 表示函數的狀態返回值,這里如果添加成功則為0
return $retval #
fi
}
for i in {1..10};do
addusers ${1}${i} # $1 為腳本傳遞的參數變量,$i 為循環體中i的變量
renum=$? # 這里的狀態返回值取決於函數中 return 的值,為0,表示添加成功,為5表示用戶存在
if [[ "$renum" -eq 5 ]];then
echo "user ${1}${i} exits."
elif [[ "$renum" -eq 0 ]];then
echo "Add user ${1}${i} successd."
else
echo "Unknow Error."
fi
done
函數變量:
變量的作用域:
環境變量:當前shell和子shell有效
本地變量:只在當前shell進程有效,為執行腳本會啟動專用子shell進程;因此本地變量的作用范圍是當前shell腳本程序文件,包括腳本中的函數
局部變量:作用域是函數的生命周期;函數結束時變量被自動銷毀
在函數中定義局部變量的方法:
local NAME=VALUE
注意:
函數中也可調用本地變量,如果函數中定義的變量名稱和本地變量相同,函數調用之后會覆蓋本地變量的值;為了避免和本地變量相沖突,使之互不干擾,所以在函數中使用的變量要使用局部變量
示例:
1、在函數中定義的本地變量和腳本中定義的本地變量名相同,但值不同的執行結果;
[root@centos7 function]# cat test.sh
#!/bin/bash
#
setname() {
name=jerry
echo "Function:$name"
}
name=tom
echo "Fun before:$name"
setname
echo "Fun after:$name"
#執行結果,可以發現 函數中定義的變量覆蓋了腳本中定義的變量,
[root@centos7 function]# bash test.sh
Fun before:tom # 腳本執行前為本地變量
Function:jerry # 調用函數,函數也會調用本地變量,並再次給其賦值
Fun after:jerry # 腳本中定義的變量被函數中定義的變量覆蓋
2、在函數中定義局部變量,和腳本中定義本地變量但值不同的結果:
[root@centos7 function]# cat test.sh
#!/bin/bash
#
setname() {
local name=jerry
echo "Function:$name"
}
name=tom
echo "Fun before:$name"
setname
echo "Fun after:$name"
#執行結果如下:
[root@centos7 function]# bash test.sh
Fun before:tom
Function:jerry
Fun after:tom
結論:
如果函數中有局部變量,如果其名稱同本地變量,使用局部變量。否則,函數執行之后會把當前shell中定義的相同的變量覆蓋。
定義的局部變量只是在函數內部使用,互不干擾;如果不加local就是本地變量了。
函數遞歸:
函數遞歸:
函數直接或間接調用自身
遞歸實例(階乘):
階乘是所有小於及等於該數的正整數的積,0的階乘為1,自然數n的階乘寫作n!。
階乘亦可以遞歸方式定義:
n! = n(n-1)! = n(n-1)(n-2)! = n(n-1)(n-2)(n-3)...1
0! = 1
示例1:給定一個正整數,求其階乘:
[root@centos7 function]# cat jiecheng.sh
#!/bin/bash
#
fab() {
if [ $1 -eq 0 -o $1 -eq 1 ];then
echo 1
else
echo $[$1*$(fab $[$1-1])]
fi
}
fab $1
#執行結果如下:
[root@centos7 function]# bash jiecheng.sh 2
2
[root@centos7 function]# bash jiecheng.sh 5
120
[root@centos7 function]# bash jiecheng.sh 3
6