1、函數和shell程序的區別
?Shell程序在子Shell中運行
?而Shell函數在當前Shell中運行。因此在當前Shell中,函數可以對shell中變量進行修改
[root@redhat7 script]#name=wang;func1 () { echo func1 is runing;echo $name;echo processid is $$; }
[root@redhat7 script]#func1
func1 is runing
wang
processid is 1410 ---函數是在當前shell中運行的,并沒有開子進程
[root@redhat7 script]#echo $$
1410
[root@redhat7 script]#name=wang;func1 () { name=mage;echo func1 is runing;echo $name;echo processid is $$; }
[root@redhat7 script]#func1
func1 is runing
mage ---因為函數執行的時候沒有開子進程,所以會影響當前進程定義的變量,會對其進行修改。
processid is 1410
[root@redhat7 script]#echo $$
1410
2、查看定義的函數
[root@redhat7 script]#declare -f |grep -A 10 func1 ---declare -f可以查看所有定義的函數
func1 ()
{
name=mage;
echo func1 is runing;
echo $name;
echo processid is $$
}
[root@redhat7 ~]#declare -f func1 ---可以不用過濾直接查看定義的函數
func1 ()
{
declare -i num=100;
echo func1 is runing;
echo "func1:num=$num"
}
[root@redhat7 script]#set |grep -A 10 func1---set可以查看所有定義的函數和變量
func1 ()
{
name=mage;
echo func1 is runing;
echo $name;
echo processid is $$
}
3、函數的組成部分
?函數由兩部分組成:函數名和函數體
?help function
?語法一:
function f_name{
...函數體...
}
?語法二: ---- 此種方法是最常用的,系統自帶的函數一般都是用這種方法定義的函數
function f_name(){
...函數體...
}
?語法三:
f_name(){
...函數體
4、別名對函數的影響
[root@redhat7 script]#declare -f |grep func1 -A 5 ---之前定義過一個這樣的函數
func1 ()
{
name=mage;
echo func1 is runing;
echo $name;
echo processid is $$
}
[root@redhat7 script]#alias func1='ls -l' ---將函數名定義為別名
[root@redhat7 script]#func1 ----當調用這個函數時,執行的是別名而不是函數本身
total 168
-rwxr-xr-x. 1 root root 156 Aug 24 18:59 192.sh
-rwxr-xr-x. 1 root root 390 Aug 25 08:23 21dengyao.sh
總結:定義別名之后函數就無法被調用了,所以一般將函數寫到腳本里使用,因為腳本里面別名是不起作用的。
4、函數的使用
?可在交互式環境下定義函數
?可將函數放在腳本文件中作為它的一部分
?可放在只包含函數的單獨文件中
?調用:函數只有被調用才會執行
調用:給定函數名
函數名出現的地方,會被自動替換為函數代碼
?函數的生命周期:被調用時創建,返回時終止
5、函數的退出狀態碼
(1) 默認取決于函數中執行的最后一條命令的退出狀態碼
(2) 自定義退出狀態碼,其格式為:
return 從函數中返回,用最后狀態命令決定返回值,退出函數不可以用exit,exit會退出整個腳本,用return可以退出函數。
return 0 無錯誤返回。
return 1-255 有錯誤返回
函數和腳本的退出狀態碼保存到$?里
unset 函數名可以銷毀函數和撤銷變量的操作一樣
set -- 銷毀位置變量
6、在腳本中定義使用函數
?函數在使用前必須定義,因此應將函數定義放在腳本開始部分,直至shell首次發現它后才能使用
?調用函數僅使用其函數名即可
示例
[root@redhat7 script]#vim func1.sh
#!/bin/bash
#
hello () {
echo "Hello there today's date is `date +%F`"
}
echo "now going to function hello"
hello ---要調用這個函數,使用函數名就可以
echo "back from the function"
[root@redhat7 script]#./func1.sh
now going to function hello
Hello there today's date is 2017-08-29
back from the function
7、使用函數文件
[root@redhat7 script]#vim funcs ----創建一個函數文件,注意函數文
件不是腳本,不用加#!/bin/bash
hello () {
echo "Hello there today's date is `date +%F`"
[root@redhat7 script]#vim func1.sh
#!/bin/bash
#
source funcs ---先把函數文件載入到當前的腳本中,也可以用
. funcs,注意中間要有空格
echo "now going to function hello"
hello ----然后調用這個函數
echo "back from the function"
[root@redhat7 script]#vim functions
hello () {
echo "Hello there today's date is `date +%F`"
}
add () {
echo $[$1+$2]
}
osversion () {
sed -r 's/.* ([0-9])\..*/\1/' /etc/redhat-release ---定義一個函
數,判斷版本號
}
[root@redhat7 script]#. functions
[root@redhat7 script]#osversion
7
[root@redhat7 script]#[ `osversion` == 7 ]&&echo 7 ---調用這個函
數,進行判斷,如果是7,就打印7
7
注意:如果函數文件和腳本文件不在同一個目錄下,此時函數文件要寫上全路徑,比如source /root/funs。使用函數文件可以把經常使用的函數都放到一個函數文件里,使用的時候先載入這個函數文件,然后調用對應的函數就可以。
8、函數變量
?變量作用域:
環境變量:當前shell和子shell有效
本地變量:只在當前shell進程有效,為執行腳本會啟動專用子shell進程;因此,本地變量的作用范圍是當前shell腳本程序文件,包括腳本中的函數
?局部變量:函數的生命周期;函數結束時變量被自動銷毀,只對函數內的變量有效。
?在函數中定義局部變量的方法
local NAME=VALUE
示例
[root@redhat7 ~]#func1 () { local name=mage;echo func1 is runing;echo "func1:name=$name"; }
[root@redhat7 ~]#func1
func1 is runing
func1:name=mage
[root@redhat7 ~]#name=wang
[root@redhat7 ~]#echo $name ---因為name在函數中被定義為局部
變量,只在函數中生效,所以不會影響當前shell中的變量
wang
總結:為了避免函數中的變量影響腳本中的其他變量,一般將函數中的變量定義為局部變量。
[root@redhat7 ~]#func1 () { declare -i num=100;echo func1 is runing;echo "func1:num=$num"; } ---declare -i 作用是聲明是整數,
但放到函數中聲明是整數的同時,也將變量聲明為局部變量,不影響
當前shell的變量
[root@redhat7 ~]#num=200
[root@redhat7 ~]#func1
func1 is runing
func1:num=100
[root@redhat7 ~]#echo $num
200
[root@redhat7 ~]#func1 () { declare -ig num=100;echo func1 is runing;echo "func1:num=$num"; } ----如果要將函數內的變量定義為
全局變量也就是本地變量,可以使用-g選項
[root@redhat7 ~]#num=300
[root@redhat7 ~]#func1
func1 is runing
func1:num=100
[root@redhat7 ~]#echo $num
100
9、環境函數
[root@redhat7 script]#vim func1.sh
#!/bin/bash
#
hello ---調用hello這個函數
[root@redhat7 script]#source /root/funcs ---先把用hello函數的funcs函數文件載入到當前shell中
[root@redhat7 script]#hello ---在當前shell中調用這個函數,會執行
這個函數
Hello there today's date is 2017-08-29
[root@redhat7 script]#./func1.sh ---但執行腳本時調用這個函數就執
行不了,說明執行腳本時是開了一個當前shell的子進程,而hello這個
函數不在子進程里,所以無法調用
./func1.sh: line 3: hello: command not found
[root@redhat7 script]#export -f hello ---聲明hello這個函數是環境函
數,就可以將函數傳給子進程,讓子進程也可以使用,當然不聲明也
可以,在當前腳本中再執行一次source /root/funcs,把函數文件載入到當前腳本
的shell中。
[root@redhat7 script]#./func1.sh
Hello there today's date is 2017-08-29
[root@redhat7 script]#
還可以使用declare -xf 聲明是環境函數,跟export -f一樣。
declare -x 聲明是環境變量=export
10、函數參數
?函數可以接受參數:
傳遞參數給函數:調用函數時,在函數名后面以空白分隔給定參數列表即可;例如“testfun carg1 arg2 ...”
在函數體中當中,可使用$1, $2, ...調用這些參數;還可以使用$@, $*, $#等特殊變量
但不能使用$0,$0表示帶路徑的當前腳本的名字,不能代表函數名,這一點和腳本不同,其他的參數調用和腳本都類似。
示例
[root@redhat7 script]#vim functions
hello () {
echo "Hello there today's date is `date +%F`"
}
add () { ---定義一個函數,讓傳給函數的參數進行加運算
echo $[$1+$2]
}
[root@redhat7 script]#vim func1.sh
#!/bin/bash
#
source functions
add 1 2 ---調用函數時加上參數
[root@redhat7 script]#./func1.sh ---執行結果
3
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)!
[root@redhat7 script]#vim jiecheng.sh
#!/bin/bash
#
fact () {
if [ $1 -eq 0 -o $1 -eq 1 ];then
echo 1
else
echo $[$1*`fact $[$1-1]`] ---函數自己調用自己
fi
}
fact $1
[root@redhat7 script]#./jiecheng.sh 4
24
12、fork炸彈
?fork炸彈是一種惡意程序,它的內部是一個不斷在fork進程的無限循環,實質是一個簡單的遞歸程序。由于程序是遞歸的,如果沒有任何限制,這會導致這個簡單的程序迅速耗盡系統里面的所有資源
?函數實現
:(){ :|:& };:
bomb() { bomb | bomb & }; bomb
?腳本實現
cat Bomb.sh
#!/bin/bash
./$0|./$0& ---腳本調用腳本
13、如何實現打印正確和錯誤時顯示ok和FAILED
[root@redhat7 script]#vim ok.sh
#!/bin/bash
#
. /etc/init.d/functions ---將這個函數文件讀入到當前腳本中
action "success! " true ---調用函數文件里的action這個函數,其中true可以用任何返回結果為真的代替
action "failed" false ---false可以用任何返回結果為假的代替
[root@redhat7 script]#./ok.sh
success! [ OK ]
failed [FAILED]