我就是认真:shell函数学习

shell函数

函数的定义

当需要重复使用一个脚本中的功能,或者脚本达到一定程度时,使用函数就比较方便。

定义函数语法:


#函数名
function_name() {
    #函数体,在函数中执行的命令行
    commands...
    #参数返回,return可有可无,如果没有,则返回函数最后一条命令运行结果作为返回值;如果有,则return后跟数值n(0~255)
    [return int;]
}

第二种方法定义函数语法:


function functionname() {

    commands... 
  
}
#如果有function关键字,()可以省略

函数体外的大括号与函数体之间必须有空白符或者换行符,因为{}时保留字,只有存在空白符或者换行符时,{}才会被识别为保留字

函数的参数、变量与返回值

向函数传递参数

函数使用特殊变量$1、$2....来访问传递的参数。

如:

[root#rs1 test]# cat func_1.sh 
#!/bin/bash

passed() {
    a=$1
    echo "passed() : \$0 is $0"

    echo "passed() : \$1 is $1"
    echo "passed() : \$a is $a"
    
    echo "passed() : total args passed to me $#"

    echo "passed() : all args (\$@) passed to me - \"$@\""
    echo "passed() : all args (\$*) passed to me - \"$*\""
}

echo "****** calling passed() first time*******"
passed one
echo "****** calling passed() second time*******"
passed one two three

运行结果:


[root#rs1 test]# bash func_1.sh 
****** calling passed() first time*******
passed() : $0 is func_1.sh
passed() : $1 is one
passed() : $a is one
passed() : total args passed to me 1
passed() : all args ($@) passed to me - "one"
passed() : all args ($*) passed to me - "one"
****** calling passed() second time*******
passed() : $0 is func_1.sh
passed() : $1 is one
passed() : $a is one
passed() : total args passed to me 3
passed() : all args ($@) passed to me - "one two three"
passed() : all args ($*) passed to me - "one two three"
  • 所有函数参数都可以通过$1、$2、...、$N,即位置参数来访问

  • $0指代Shell脚本的名字

  • $*或者$@保存传递给函数的所有参数

  • $#保存传递给函数的位置参数的个数

本地变量

默认情况下脚本所有的变量都是全局的,在函数中修改一个变量将改变这个脚本中此变量的值。

如:


[root#rs1 test]# cat native_fuc.sh 
#!/bin/bash

#定义函数
create_logFile() {

    d=$1
    echo "create_logFile(): d is set to $d"

}

#定义变量d
d=/var/log/message
echo "Before calling create_logFile d is set to $d"
#调用函数create_logFile,并指定一个参数
create_logFile "/etc/passwd"
echo "After calling create_logFile d is set to $d"

执行结果:


[root#rs1 test]# bash native_fuc.sh 
Before calling create_logFile d is set to /var/log/message
create_logFile(): d is set to /etc/passwd
After calling create_logFile d is set to /etc/passwd

如果只想函数传递的参数被函数体本身使用,而不更改全局变量的值,则可以添加local关键字

如:

[root#rs1 test]# cat native_fuc.sh 
#!/bin/bash

#定义函数
create_logFile() {

    local d=$1
    #d=$1
    echo "create_logFile(): d is set to $d"

}

#定义变量d
d=/var/log/message
echo "Before calling create_logFile d is set to $d"
#调用函数create_logFile,并指定一个参数
create_logFile "/etc/passwd"
echo "After calling create_logFile d is set to $d"

运行结果:

[root#rs1 test]# bash native_fuc.sh "/etc/passwd"
Before calling create_logFile d is set to /var/log/message
create_logFile(): d is set to /etc/passwd
After calling create_logFile d is set to /var/log/message
  • local只能在函数内部使用

  • local命令将变量名的可见范围限制在函数内部

使用return命令

如果在函数里面有Shell内置命令return,则函数执行到return语句时结束,并返回到Shell脚本中调用函数位置的下一个命令。如果return带有一个数值型参数,则这个参数就是函数的返回值,该值范围:0~255;否则,函数的返回值是函数体内最后一个执行的命令的返回状态。

如:

[root@rs1 test]# cat func_return.sh 
#!/bin/bash

checkpid() {

#定义本地变量i
    local i
#使用for循环遍历传递给此函数的所有参数
    for i in $*
    do
        #如果目录/proc/$i存在,则执行"存在"语句、否则执行"不存在"语句
        #每个进程的pid在/proc/目录下,都会有一个以pid号命名的目录来维护进程

        test -d "/proc/$i" && echo "pid:$i 存在" || echo "pid:$i 不存在"
    done
}
checkpid 3027 24781 24789 1 

执行结果:

[root@rs1 test]# bash func_return.sh 
pid:3027 存在
pid:24781 不存在
pid:24789 不存在
pid:1 存在

函数返回值测试

可以直接在脚本调用函数语句的后面(必须紧跟着)使用Shell特殊参数"?"来测试函数调用的返回值,通过特殊参数"?"可以得到最近一次执行的前台命令的退出状态。

如:


[root@rs1 test]# cat func_return_1.sh 
#!/bin/bash

checkpid() {

#定义本地变量i
    local i
#使用for循环遍历传递给此函数的所有参数
    for i in $*
    do
        #如果目录/proc/$i存在,则执行"存在"语句、否则执行"不存在"语句
        #每个进程的pid在/proc/目录下,都会有一个以pid号命名的目录来维护进程
        test -d /proc/$i
        if [ $? = 0 ]
        then 
            echo "The $i is running"
        else
            echo "The $i is not running"
        fi

    done
}
checkpid $*

执行结果:


[root@rs1 test]# bash func_return_1.sh 1 222333 1803
The 1 is running
The 222333 is not running
The 1803 is running

函数的调用

在Shell命令行调用函数

在命令行中,可以通过直接输入函数的名字,来调用或引用函数。

如:


[root@rs1 test]# yday (){ date --date='1 day ago';  }
[root@rs1 test]# yday
Thu Jul  5 16:05:47 CST 2018

在脚本中调用函数

在脚本中定义并调用一个函数方法:

[root@rs1 test]# cat func_call.sh 
#!/bin/bash

yday() {

    #显示一天前的日期和时间
    date --date='1 day ago'

}
#调用函数
yday

执行脚本:


[root@rs1 test]# bash func_call.sh 
Thu Jul  5 17:51:36 CST 2018

要在脚本中调用函数,首先要创建函数,并确保函数定义的位置比调用函数更靠前。否则会执行失败:


[root@rs1 test]# cat func_call_erro.sh 
#!/bin/bash

#调用函数
yday
yday() {

    #显示一天前的日期和时间
    date --date='1 day ago'

}
[root@rs1 test]# bash func_call_erro.sh 
func_call_erro.sh: line 4: yday: command not found

从函数文件中调用函数

可以将所有的函数存储在一个函数文件中,然后将函数加载到当前脚本或者命令行。

加载函数文件中所有函数的方法:


. /path/to/funcution.sh

如:

创建一个函数文件:

[root@rs1 test]# cat my_func.sh 
#!/bin/bash

#定义变量
declare -r TURE=0
declare -r FALSE=1
declare -r PASSWD_FILE=/etc/passwd

#用途:将字符串转化为小写
#参数:
#   $1 -> 要转换为小写的字符串

function to_lower() {

    #定义本地变量str
    local str="$@"
    #定义本地变量output
    local output
    #将变量str的值转换为小写,然后赋值给output变量
    output=$(tr '[A-Z]' '[a-z]' <<< "${str}")
    echo $output
}

#用途:如果是root用户执行则返回true
#参数:无
#返回值:True或False
function is_root() {

    #如果运行次脚本的用户uid等于0,则返回0,否则返回1
    if test $(id -u) -eq 0
    then
    return $True
    else
    return $False
    fi
}
#用途:如果用户名存在于文件/etc/passwd中,则返回true
#参数:$1(用户名) -> 要在文件/etc/passwd中检查用户名
#返回值:True或False
function is_user_exits() {

    #定义本地变量u
    local u=$1
    #如果文件/etc/passwd中存在以变量$1的值为开头行,则返回0,否则返回1
    grep -q "^${u}" $PASSWD_FILE && return $True || return $False

}

创建一个脚本,在脚本中调用函数文件:


[root@rs1 test]# cat func_demo.sh 
#!/bin/bash

#加载函数文件my_func.sh
#根据自己文件路径进行加载
. /mnt/test/my_func.sh

#定义一个本地变量用于测试
var="I Like You But Just Like You."

#调用函数is_root,执行成功或失败,打印不同结果
is_root && echo "You are logged in as root." || echo "You are not logged in as root."

#调用函数is_user_exits
is_user_exits `whoami` && echo "Account found." || echo "Account not found."

#打印修改前var的值
echo -e "*** Orignal quote: \n${var}"

#调用函数to_lower()
#将$var作为参数传递给函数to_lower()
#在echo内使用命令替换
echo -e "*** Lowercase version: \n$(to_lower ${var})"

执行结果:

以root用户身份运行:

[root@rs1 test]# whoami 
root
[root@rs1 test]# bash func_demo.sh 
You are logged in as root.
Account found.
*** Orignal quote: 
I Like You But Just Like You.
*** Lowercase version: 
i like you but just like you.

切换成别的用户:

[kiosk@rs1 test]$ whoami 
kiosk
[kiosk@rs1 test]$ bash func_demo.sh 
You are not logged in as root.
Account found.
*** Orignal quote: 
I Like You But Just Like You.
*** Lowercase version: 
i like you but just like you.

递归函数调用

递归函数是重复调用函数本身的函数,并且没有递归调用次数的限制。

一个递归函数调用的例子:


[root@rs1 test2]# cat fact.sh 
#!/bin/bash

#定义函数factorial(计算给丁命令行参数的阶乘)
factorial() {

    #定义本地变量i
    local i=$1
    #定义本地变量f
    local f
    #声明变量i为整数
    declare -i i
    #声明变量f为整数
    declare -i f

    #factorial函数被调用直到$f的值<=2
    #开始递归
    test $i -le 2 && echo $i || { f=$(( i - 1));f=$(factorial $f);f=$((f * i )); echo $f; }
}
#显示函数用法
    test $# -eq 0 && { echo "Usage: $0 number"; exit 1; }

#调用函数
    factorial $1

执行脚本:


[root@rs1 test2]# bash -x fact.sh 3
+ test 1 -eq 0
+ factorial 3
+ local i=3
+ local f
+ declare -i i
+ declare -i f
+ test 3 -le 2
+ f=2
++ factorial 2  //到这里进行递归
++ local i=2
++ local f
++ declare -i i
++ declare -i f
++ test 2 -le 2 //到这个条件递归结束
++ echo 2
+ f=2
+ f=6   //f = f * i 此时,f=2,i=3
+ echo 6
6
[root@rs1 test2]# bash fact.sh 4
24

函数后台运行

"&"操作符可以将命令放在后台运行并释放当前终端。同样也可以将函数放在后台运行/

定义语法规则如下:


#定义函数name
name() {
  
  echo "Do something"
  sleep 1
  
}
#将函数放在后台运行
name &
#继续执行其他命令
...

如:


[root@rs1 test2]# cat progressdots.sh 
#!/bin/bash

#定义函数progress,显示进度条
progress() {

#   echo -n "$0:Please wait"
    #执行无限while循环
    while true
    do
        echo -n "."
        #休眠2秒
        sleep 2
    done
}
#定义函数dobackup
dobackup() {

    sleep 10
}

#将函数放在后台运行
progress &

#保存函数progress()运行的进程号
#需要使用PID来结束该函数
MYSELF=$!
#转移控制到函数dobackup
dobackup

#杀死进程
kill $MYSELF > /dev/null 2>&1

echo -n "done"

执行结果:


[root@rs1 test2]# bash progressdots.sh 
.....done
//总共用时10秒,每个点隔二秒出现一次

总结

Shell中函数的使用,可以:

  • 节省大量时间

  • 避免代码编写的冗余

  • 更容易的编写程序

  • 非常简单的维护程序

  • 函数也是DevoPS的一部分


猜你喜欢

转载自blog.csdn.net/fsx2550553488/article/details/80947989