设为首页 收藏本站
查看: 875|回复: 0

Linux中的shell脚本编程之函数

[复制链接]

尚未签到

发表于 2018-8-25 09:50:57 | 显示全部楼层 |阅读模式
  shell脚本编程之函数
  ==============================================================================
  概述:
  本篇将主要讲解shell脚本编程中函数的用法,具体内容如下:
  ==============================================================================
函数:function
  
  1.函数介绍
  
  ★函数:
  定义:

  •   把一段独立功能的的代码当做一个整体,并为之一个名字,命名的代码段,此即为函数;
  功能:

  •   函数function是由若干条shell命令组成的语句块,实现代码重用和模块化编程。
  注意:

  •   定义函数的代码段不会自动执行,在调用时执行;所谓函数调用,就在代码中给定函数名称即可;
  •   函数名出现的任何位置,在代码执行时,都会被自动替换为函数代码;
  函数和shell程序比较相似,区别在于:

  •   Shell程序在子Shell中运行;
  •   而Shell函数在当前Shell中运行。因此在当前Shell中,函数可以对shell中变量进行修改;
  •   它与shell程序形式上是相似的,不同的是它不是一个单独的进程,不能独立运行,而是shell程序的一部分。
  2.函数语法,定义和使用
  
  ★语法:
  函数由两部分组成:函数名和函数体。
  语法一:
  function f_name {
  ...函数体...
  }
  语法二:
  f_name() {
  ...函数体...
  }
  ★函数的定义和使用:
  可在交互式环境下定义函数:

  •   可将函数放在脚本文件中作为它的一部分(自己定义自己用)
  •   可放在只包含函数的单独文件中
  调用:函数只有被调用才会执行;

  •   调用方法:在代码中给定函数名称,函数名出现的地方,会被自动替换为函数代码;
  函数的生命周期:

  •   被调用时创建,返回时终止;
  3.函数有两种返回值
  
  ★函数的执行结果返回值:

  •   使用echo或printf命令进行输出;
  •   函数体中调用命令的输出结果;
  ★函数的退出状态码:
  其状态返回结果为函数中执行的最后一条命令的状态结果;
  自定义退出状态码:return

  •   return [0-255] :从函数中返回,用最后状态命令决定返回值;
  •   0:成功,无错误返回
  •   1-255:失败,有错误返回
  示例:
  1.给定一个用户名,显示其用户名,id和默认的shell
[root@centos7 function]# cat userinfo.sh  
#!/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
  1.以函数的方式改写服务框架脚本
[root@centos7 function]# cat testservice  
#!/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.交互环境下定义和使用函数:
  示例:
  1.定义该函数后,键入fun,其显示结果同命令#who,#hostname的作用相同。这里要注意优先级,如果函数和定义的别名相同(如:alias fun =ls),别名的优先级高。 优先级:别名>函数>内部命令>外部命令
  该fun函数将一直保留到用户从系统退出,或执行了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。
  示例:
[root@centos7 function]# cat test1.sh  
#!/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
  2.使用函数文件定义函数
  
  ★可以将经常使用的函数存入函数文件,然后将函数文件载入shell。

  •   文件名可任意选取,但最好与相关任务有某种联系。例如:functions.main
  •   一旦函数文件载入(source或.)shell,就可以在命令行或脚本中调用函数。可以使用set命令查看所有定义的函数,其输出列表包括已经载入shell的所有函数。
  •   若要改动函数,首先用unset命令从shell中删除函数。改动完毕后,再重新载入此文件
  函数的载入,执行,检查和删除

  •   函数文件已创建好后,要将它载入shell,使用 source 或  "."
  定位函数文件并载入shell的格式:

  •   . /PATH/TO/SOMEFILE (  )或者 source /PATH/TO/SOMEFILE
  注意:

  •   这里的文件名要带正确路径(绝对或相对路径)
  查看是否载入:

  •   用source或 . 载入shell后,可使用set命令检查函数是否已载入。set命令将在shell中显示所有的载入函数;
  删除shell函数:

  •   现在对函数做一些改动。首先删除函数,使其对shell不可用。使用unset命令完成此功能。
  •   命令格式为:unset function_name
  在脚本中载入函数

  •   在脚本中使用函数,只要source FILES或者. FILE (存放函数的文件),就可以载入到当前shell当中,调用函数了。
  注意:

  •   Shell函数在当前Shell中运行。因此在当前Shell中,函数可以对shell中变量进行修改;
  •   当函数载入到当前环境中或脚本中时,当前环境或者脚本中定义的变量会和函数中定义的变量起冲突,这是因为,我们在当前环境中或者脚本中定义的为本地变量,函数载入当前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.添加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
  

  
#执行过程如下:
  
[root@centos7 function]# bash -x useradd.sh ab
  
+ for i in '{1..10}'
  
+ addusers ab1
  
+ id ab1
  
+ useradd ab1
  
+ retval=0
  
+ return 0
  
+ retval=0
  
+ [[ 0 -eq 5 ]]
  
+ [[ 0 -eq 0 ]]
  
+ echo 'Add user ab1 successd.'
  
Add user ab1 successd.
  
+ for i in '{1..10}'
  
+ addusers ab2
  
+ id ab2
  
+ useradd ab2
  
+ retval=0
  
+ return 0
  
+ retval=0
  
+ [[ 0 -eq 5 ]]
  
+ [[ 0 -eq 0 ]]
  
+ echo 'Add user ab2 successd.'
  
Add user ab2 successd.
  示例2:
[root@centos7 function]# cat jian.sh  
#!/bin/bash
  
#
  

  
jian() {
  a=$[$1-$2]
  echo $a
  
}
  

  
echo "1st arg is $1"  # 脚本传递的参数
  
echo "2st arg is $2"
  

  
jian $2 $1  # 脚本传递给函数的位置参数
  

  
# 传递参数,执行结果如下:
  
[root@centos7 function]# bash jian.sh 20 10
  
1st arg is 20
  
2st arg is 10
  
-10
  

  
[root@centos7 function]# bash jian.sh 10 20
  
1st arg is 10
  
2st arg is 20
  
10
  

  
[root@centos7 function]# bash -x jian.sh 10 20
  
+ echo '1st arg is 10'
  
1st arg is 10
  
+ echo '2st arg is 20'
  
2st arg is 20
  
+ jian 20 10
  
+ a=10
  
+ echo 10
  
10
  

  
# 如上,可以看出,传递给脚本的第一个参数为10,第二个参数为20,但脚本传递给函数的第一个参数
  
# 是外部传递给脚本的第二个参数。也就是说函数只关心传递给函数名的参数的位置,而不关心传递给
  
# 脚本参数的位置
函数变量:
  
  ★变量的作用域:

  •   环境变量:当前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
  结论:
  1.如果函数中有局部变量,如果其名称同本地变量,使用局部变量。否则,函数执行之后会把当前shell中定义的相同的变量覆盖。
  2.定义的局部变量只是在函数内部使用,互不干扰;如果不加local就是本地变量了。
  
函数递归:
  
  ★函数递归:

  •   函数直接或间接调用自身
  递归实例(阶乘):

  •   阶乘是基斯顿·卡曼于1808 年发明的运算符号,是数学术语一个正整数的阶乘(factorial)是所有小于及等于该数的正整数的积,并且有0的阶乘为1,自然数n的阶乘写作n!。
  阶乘亦可以递归方式定义:
  0!=1,n!=(n-1)!×n。
  n!=n(n-1)(n-2)...1
  n(n-1)! = n(n-1)(n-2)!
  示例:
  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
  

  
[root@centos7 function]# bash -x jiecheng.sh 5
  
+ fab 5
  
+ '[' 5 -eq 0 -o 5 -eq 1 ']'
  
++ fab 4
  
++ '[' 4 -eq 0 -o 4 -eq 1 ']'
  
+++ fab 3
  
+++ '[' 3 -eq 0 -o 3 -eq 1 ']'
  
++++ fab 2
  
++++ '[' 2 -eq 0 -o 2 -eq 1 ']'
  
+++++ fab 1
  
+++++ '[' 1 -eq 0 -o 1 -eq 1 ']'
  
+++++ echo 1
  
++++ echo 2
  
+++ echo 6
  
++ echo 24
  
+ echo 120
  
120
  

  
#可以看到,一层一层的进去,再一层一层的退出来,到底才有值,中间没有值
匿名函数:
  示例:
DSC0000.png

  
  练习:
  1.编写一个脚本/root/bin/copycmd.sh
  (1) 提示用户输入一个可执行命令名称;
  (2) 获取此命令所依赖到的所有库文件列表
  (3) 复制命令至某目标目录(例如/mnt/sysroot)下的对应路径下;
  如:/bin/bash ==> /mnt/sysroot/bin/bash
  /usr/bin/passwd==> /mnt/sysroot/usr/bin/passwd
  (4) 复制此命令依赖到的所有库文件至目标目录下的对应路径下:
  如:/lib64/ld-linux-x86-64.so.2 ==> /mnt/sysroot/lib64/ld-linux-x86-                                                                               64.so.2
  (5)每次复制完成一个命令后,不要退出,而是提示用户键入新的要复制的命令,并重复完成上述功能;直到用户输入quit退出
  2、写一个函数实现两个数字做为参数,返回最大值
  3、写一个函数实现数字的加减乘除运算,例如输入1 + 2,,将得出正确结果
  4、斐波那契数列又称黄金分割数列,因数学家列昂纳多·斐波那契以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……,斐波纳契数列以如下被以递归的方法定义:F(0)=0,F(1)=1,F(n)=F(n-1)+F(n-2)(n≥2)
  写一个函数,求n阶斐波那契数列
DSC0001.png

  



运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-556247-1-1.html 上篇帖子: 自动化部署java maven项目到多个目标主机的Shell脚本 下篇帖子: 第一次看到Spark崩溃:Spark Shell内存OOM的现象!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表