2018-04-17 脚本进阶3 函数

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

总结:定义别名之后函数就无法被调用了,所以一般将函数写到脚本里使用,因为脚本里面别名是不起作用的。

5、函数的使用

可在交互式环境下定义函数
可将函数放在脚本文件中作为它的一部分
可放在只包含函数的单独文件中
调用:函数只有被调用才会执行
调用:给定函数名
函数名出现的地方,会被自动替换为函数代码
函数的生命周期:被调用时创建,返回时终止

6、函数的退出状态码

(1) 默认取决于函数中执行的最后一条命令的退出状态码
(2) 自定义退出状态码,其格式为:
return 从函数中返回,用最后状态命令决定返回值,退出函数不可以用exit,exit会退出整个脚本,用return可以退出函数。
return 0 无错误返回。
return 1-255 有错误返回
函数和脚本的退出状态码保存到$?里
unset 函数名可以销毁函数和撤销变量的操作一样
set -- 销毁位置变量

7、在脚本中定义使用函数

函数在使用前必须定义,因此应将函数定义放在脚本开始部分,直至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

8、使用函数文件

[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。使用函数文件可以把经常使用的函数都放到一个函数文件里,使用的时候先载入这个函数文件,然后调用对应的函数就可以。

9、函数变量

变量作用域:
环境变量:当前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

10、环境函数

[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

11、函数参数

函数可以接受参数:
传递参数给函数:调用函数时,在函数名后面以空白分隔给定参数列表即可;例如“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

12、函数递归

函数直接或间接调用自身
注意递归层数
递归实例:
阶乘是基斯顿·卡曼于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

13、fork炸弹

fork炸弹是一种恶意程序,它的内部是一个不断在fork进程的无限循环,实质是一个简单的递归程序。由于程序是递归的,如果没有任何限制,这会导致这个简单的程序迅速耗尽系统里面的所有资源
函数实现
:(){ :|:& };:
bomb() { bomb | bomb & }; bomb
脚本实现

cat Bomb.sh
#!/bin/bash
./$0|./$0&   ---脚本调用脚本

14、如何实现打印正确和错误时显示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]

你可能感兴趣的:(2018-04-17 脚本进阶3 函数)