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

Shell脚本的模块化和脚本复用

[复制链接]

尚未签到

发表于 2018-8-21 13:09:06 | 显示全部楼层 |阅读模式
  日常工作中编写shell脚本是再平常不过的了,那编写过这么多的shell,当我们遇见曾经写过的相关的处理函数时,如何来复用?直接拷贝相应的代码还是“引用过来”,或者是在编写一个功能相对复杂的shell时,如何做到模块化,如何组织复杂的函数调用?
  如何导入复用和模块化,就是这里要一起学习的。
  我们会按照以下几个部分来学习:
  1、shell执行方式不同的效果不同
  2、导入模块
  
  1、shell执行方式不同的效果不同
  第一种:sh  test.sh
  这种方式会在我们当前进程中,再新建一个子进行去执行这个test.sh 脚本。我们能获取到的东西,也就是这个test.sh执行完的结果或者输出。
  root@docker-host-03:~#more test.sh
  #!/bin/bash
  s="thisis a test file"
  root@docker-host-03:~#sh test.sh
  root@docker-host-03:~#echo $s
  root@docker-host-03:~#sh test.sh
  root@docker-host-03:~#echo $?
  0
  第二种:sourcetest.sh  或者.  Test.sh
  这种方式相当于是将test.sh的内容拷贝到当前进程中来执行。这样我们能获取到的内容就不仅仅是这个test.sh执行完成的结果。我们还能获取到在这个脚本中定义的全局变量,定义的功能函数。因为我们是将脚本的内容拷贝到当前文件中。
  那这种方式就和我们很多编程语言中的模块导入类似。我们来验证下是不是。
  root@docker-host-03:~#. test.sh
  root@docker-host-03:~#echo $s
  this is atest file
  root@docker-host-03:~#s=0
  root@docker-host-03:~#echo $s
  0
  root@docker-host-03:~#source  test.sh
  root@docker-host-03:~#echo $s
  this is a test file
  #我们可以看到这里,可以获取到变量s的内容。
  2、导入模块
  2.1)先导入一个最简单的
  基于上面第一点我们提到的两种shell的执行方式中的第二种。我们利用这种“拷贝”的特性就能够实现将以前写过的功能,进行导入
  比如我们写了很简单格式化输出log的函数:
  root@docker-host-03:~# cat test1.sh
  #!/bin/bash
  MODULENAME=$(basename $0)
  LOGFILE=/tmp/logfile-`date +%Y%m%d`
  log_info()
  {
  #[2017-03-31 12:00:00 ] - TextName - The log message
  Localdatetime=`date "+%Y-%m-%d %H:%M:%S"`
  if[ "$1" ];then
  echo "[ ${datetime} ] - ${MODULENAME} - $1 " | tee -a${LOGFILE}
  else
  return 1
  fi
  }
  上面这个log_info 函数会格式化刷出我们的日志内容。我们可以在多处复用。
  我们这里的Main.sh脚本和test1.sh在同一个目录里面。我们main.sh先导入test1.sh的内容,然后使用test1.sh中已有的变量,功能函数。我们来看下main.sh的内容。
  root@docker-host-03:~# cat main.sh
  #!/bin/bash
  #我们在这里通过.  Test1.sh 或是source test1.sh的方式将test1.sh导入进来。
  . test1.sh
  main()
  {
  #我们在这里就可以直接使用log_info函数了。
  log_info 'This is the main shell'
  }
  main
  root@docker-host-03:~# ./main.sh
  [ 2017-03-31 15:11:23 ] - main.sh - This isthe main shell
  root@docker-host-03:~# more/tmp/logfile-20170331
  [ 2017-03-31 15:11:39 ] - main.sh - This isthe main shell
  2.2)如果我们需要从其他地方导入其他函数
  上面我们的main和test1是在同一个目录中,所以我们在main 中使用的是相对路径。如果我们需要导入的“模块”test1.sh 是放在其他的目录?或者说我们的模块存在层级关系?我们有应该如何来引入呢?
  1)因我们在引入的时候,系统默认会在环境变量$PATH中去寻找我们要导入的文件。一种做法是我们将我们需要用到的公共模块放到$PATH包含的路径中。或者我们添加一个公用的路径到$PATH环境变量中,然后将我们需要用到的公有模块放到公用文件夹下。
  如:
  root@docker-host-03:~/test_shell# cat  main.sh
  #!/bin/bash
  . test1.sh
  . Config.sh
  main()
  {
  log_info 'This is the main shell'
  readConf
  echo${conf}
  }
  main
  root@docker-host-03:~/test_shell# echo$PATH
  /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
  #在这里我们可以看到我们将两个模块放在了PATH的路径下面。
  root@docker-host-03:~/test_shell# ls/usr/local/bin/test1.sh
  /usr/local/bin/test1.sh
  root@docker-host-03:~/test_shell# ls -l/usr/local/bin/Config.sh
  -rw-r--r-- 1 root root 59 Mar 31 15:41/usr/local/bin/Config.sh
  这里我直接将文件拷贝到了$PATH有包含的目录中。
  2)  我们将模块所在的绝对路径export到PATH中:
  如:
  root@docker-host-03:~/test_shell/module#pwd
  /root/test_shell/module
  root@docker-host-03:~/test_shell/module#tree
  .
  ├── Config.sh
  └── test1.sh
  0 directories, 2 files
  root@docker-host-03:~/test_shell/module# cd../
  root@docker-host-03:~/test_shell# moremain.sh
  #!/bin/bash
  #这里我们给了一个绝对路径
  MODUPATH=/root/test_shell/module
  export PATH=$PATH:${MODUPATH}
  . test1.sh
  . Config.sh
  main()
  {
  log_info 'This is the main shell'
  readConf
  echo${conf}
  }
  Main
  3) 动态的加载模块
  当然如果我们在编写一个功能相对复杂的shell的时候,我们将会将这个一个整个复杂的带有结构关系的shell打包,拷贝到任何一个地方去执行。这样我们就没有办法事先知道我们的绝对路径是怎样的。比如这样的目录结构:
  root@docker-host-03:~/test_shell#pwd
  /root/test_shell
  root@docker-host-03:~/test_shell#tree
  .
  ├── main.sh
  ├── module
  │   ├── Config.sh
  │   └── test1.sh
  └── module2
  └── mail.sh
  2directories, 4 files
  root@docker-host-03:~/test_shell#more main.sh
  #!/bin/bash
  #这里动态的获取到我们的绝对路径,并把模块的路径拼凑出来。
  MODUPATH=$(dirname$(readlink -f $0))/module
  echo${MODUPATH}
  exportPATH=$PATH:${MODUPATH}
  .test1.sh
  .Config.sh
  main()
  {
  log_info 'This is the main shell'
  readConf
  echo ${conf}
  }
  Main
  4)如何避免重复多次的导入某个模块
  我们要说source,其实就是将内容拷贝到同一个地方执行,那我们可以在模块中加入一个判断条件。如果发现我们模块中的某个变量已经存在,我们就return出去,不再导入后续的内容。
  比如这样:
  root@docker-host-03:~/test_shell#more module/test1.sh
  #!/bin/bash
  #这里我注释掉的内容就是。而且这个变量名称,尽量按照某种规则确保是唯一的变量名称。
  #if [${m_log} ];then
  #   return 0
  #fi
  #m_log="m_log"
  #这一行输出是为了测试是否多次导入了。
  echo"TEST IS"
  MODULENAME=$(basename$0)
  LOGFILE=/tmp/logfile-`date+%Y%m%d`
  log_info()
  {
  #[ 2017-03-31 12:00:00 ] - TextName - The logmessage
  datetime=`date "+%Y-%m-%d%H:%M:%S"`
  if [ "$1" ];then
  echo "[ ${datetime} ] - ${MODULENAME}- $1 " | tee -a ${LOGFILE}
  else
  return 1
  fi
  }
  这里我们在注释掉的情况下,看看多次导入是什么反应。
  root@docker-host-03:~/test_shell# more  main.sh
  #!/bin/bash
  MODUPATH=$(dirname $(readlink -f$0))/module
  echo ${MODUPATH}
  export PATH=$PATH:${MODUPATH}
  . test1.sh
  . Config.sh
  main()
  {
  log_info 'This is the main shell'
  readConf
  echo${conf}
  }
  main
  root@docker-host-03:~/test_shell# moremodule/Config.sh
  #!/bin/bash
  . test1.sh
  readConf()
  {
  conf="Read the config file"
  }
  root@docker-host-03:~/test_shell# ./main.sh
  /root/test_shell/module
  TEST IS
  TEST IS
  [ 2017-03-31 16:51:09 ] - main.sh - This isthe main shell
  Read the config file
  #我们可以看到这里多次输出了TEST IS内容。
  #将我们上面注释掉的内容去除,现在虽多次导入,但实质上只会导入一次。这样也会避免多次循环引用。
  root@docker-host-03:~/test_shell# ./main.sh
  /root/test_shell/module
  TEST IS
  [ 2017-03-31 16:56:31 ] - main.sh - This isthe main shell
  Read the config file
  #当我们去掉了注释部分,这里就不会出现多次重复载入了。
  -----------------------------小结-----------------
  其实这里的模块复用,其实就是通过source 或则. 的方式将原本的模块内容“拷贝过来”,只是在使用的过程当中,需要注意模块路径的问题,还有层级,和多次调用的问题。
  至于功能复杂的shell,我想基本也不会复杂到无法接受,基本按照一定的组织发方式就能变得清晰。并且提高复用。
  大家可以自己积累一些比较常用的函数,需要用到的时候,导入,不用重复编写。比如日志,邮件告警等等。
  ----------------------------------------------------


运维网声明 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.iyunv.com/thread-554718-1-1.html 上篇帖子: shell练习题:使用for循环批量修改文件扩展名 下篇帖子: Linux shell脚本编程详解及应用实例
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

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

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

扫描微信二维码查看详情

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


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


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


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



合作伙伴: 青云cloud

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