当前位置: 首页 > 科技观察

如何复用外部shell脚本

时间:2023-03-18 13:09:43 科技观察

本文转载自微信公众号《Linux开发那些事》,作者LinuxThings。转载此文请联系Linux开发那些事公众号.在Linux开发中,经常会编写shell脚本来执行一些任务。通常,一个脚本只做一件事。随着任务的增多,脚本会越来越多,可复用的地方也会逐渐增多。这时候就需要将脚本中的公共函数提取出来,放到一个通用的脚本中,可以被其他脚本复用。本文介绍如何在shell脚本中执行外部脚本,如何调用外部脚本中的函数,以及脚本复用。执行外部脚本的方法如果当前目录下有a.sh脚本,内容如下#!/bin/bashecho"a.sh..."脚本中执行外部脚本有几种方法source外部脚本名当前目录下的b.sh脚本,内容如下:#!/bin/bashsourcea.shecho"b.sh..."执行./b.sh,结果如下[root@ecs-centos-7~]#。/b.sha.sh...b.sh...脚本中的sourcea.sh命令会先执行当前目录下的a.sh脚本,所以结果会先输出a.sh...和那么b.sh脚本本身的打印点就是外部脚本的名称。将b.sh脚本中执行a.sh脚本的语句改为点+空格+a.sh。修改后的脚本内容如下:注意:点和a.sh之间一定要加空格,否则执行#!/bin/bash.a.shecho"b.sh..."会报错执行./b.sh,结果如下[root@ecs-centos-7~]#./b.sha.sh...b.sh...在上面的脚本中,.a.sh会执行a.sh脚本,结果会先输出a.sh...,再输出b.sh...sh外部脚本名sh外部脚本名和./外部脚本名两种方法是一样的。选择哪种方法并不重要。下面以前面的方法为例,说明将b.sh脚本中的sourcea.sh修改为sha.sh,修改后的脚本内容如下:#!/bin/bashsha.shecho"b.sh...”执行./b.sh命令,结果如下[root@ecs-centos-7~]#./b.sha.sh...b.sh...可以看到结果输出与上述两种方法相同。这三种方法有什么区别?调用外部脚本有源外部脚本和点sourcescript,shexternalscript,它们有什么区别?其中,source外部脚本和点外部脚本这两个方法是一样的,当前脚本继承外部脚本的全局变量和函数,相当于把外部脚本的函数和全局变量导入进去当前脚本修改a.sh和b.sh脚本,内容如下a.shscript#!/bin/bashVAR_A=10func_a(){echo"a.sh...pid:$$,param:$1"}b.shscript#!/bin/bashsourcea.shfunc_a$1echo"vara:$VAR_A"echo"b.sh...pid:$$"执行./b.sh5条命令,结果如下[root@ecs-centos-7~]#./b.sh5a.sh...pid:21485,param:5vara:10b.sh...pid:21485两个脚本中的$$指的是执行script从结果可以看出a.sh和b.sh是在同一个进程中执行的,所以在b.sh脚本中执行sourcea.sh命令会替换掉全局变量VAR_A和函数func_a的导入进入b.sh,打印b.sh中的变量VAR_A,输出值为和a.sh中一样,调用func_a函数,输出也显示a.sh中的函数source调用Externalscriptdot和dotexternalscript这两个方法是一样的,所以改sourcea.sh在b.shto.a.sh中,执行./b.sh5,结果还是一样,因为sh外部脚本的方法是当前脚本和外部脚本是在两个不同的进程中执行的,所以当前脚本不能直接使用外部脚本中的函数和全局变量来修改a.sh和b.sh脚本。内容如下:a.shscript#!/bin/bashtest_a(){echo"a.sh...test_a"}echo"a.sh...pid:$$"b.shscript#!/bin/bashsha.shecho"b.sh...pid:$$"test_a执行./b.sh命令,结果如下[root@ecs-centos-7~]#./b.sha.sh...pid:21818b.sh...pid:21817./b.sh:line7:test_a:commandnotfound从结果可以看出执行a.sh和b.sh的进程ID不同,b.sh脚本进程找不到test_a函数,所以在b.sh中调用test_a函数会提示找不到调用外部脚本中函数的命令。上一节提到sh外部脚本方式不能直接使用外部脚本中的函数和全局变量。这里有几种方法可以解决这个问题case分支选择这种方法类似于程序代码中的switchcase语句。通过开关选择不同的分支执行不同的逻辑。case关键字用于执行shell脚本中的a.sh脚本#!/bin/bashVAR_A=10test_a(){echo"test_a..pid:$$,p1:$1,p2:$2"}get_var(){echo${VAR_A}}case"$1"inta)test_a$2$3;;var)get_var;;*)echo"parametererr..."esacb.shscript#!/bin/bashecho"b.sh...pid:$$"sha.shta35ret=$(sha.shvar)echo"ret:$ret"执行./b.sh命令,结果如下[root@ecs-centos-7~]#./b.shb.sh...pid:24813test_a..pid:24814,p1:3,p2:5ret:10script开头b.sh打印调用进程IDsha.shta35该语句是调用a。sh脚本,传入的三个参数分别是ta、3、5。执行a.sh时,首先传入一个参数ta大小写匹配后调用test_a函数,将其余两个参数3和5作为参数传入函数ret=$(sha.shvar)语句调用a.sh脚本并传入一个var参数,大小写匹配后调用get_var函数,该函数的功能输出全局变量VAR_A的值在脚本中,$()语句中的作用是获取()中命令的返回值,这里是a.sh脚本中的get_var函数将ret变量的返回值赋值给ret变量,所以变量的值就是a.sh脚本中全局变量VAR_A的值说明:如果要获取函数返回值,可以在函数中使用echo打印对应的输出值,然后使用$(函数名参数列表)获取函数中打印的值,如ret=$(sha.shvar)b.sh脚本语句上面,变量ret的值是a.sh脚本中get_var函数输出的值10。这里需要注意的是,如果函数中有echodebuglog,那么debuglog也会在函数调用模板中连同上面描述的case关键字一起返回。匹配调用不同的函数有一个缺点。每次在a.sh脚本中添加一个函数,都需要在case中添加一个分支,在分支中调用不同的函数。还需要注意函数是否传入了参数,参数个数是否正确。我们可以通过在每个外部调用脚本末尾添加如下语句来解决上述问题,具体语句如下if[$#-ge1];thenname="$1"shift1$name"$@"fi上述语句首先判断调用脚本时传入的参数个数,只有参数个数大于等于1才有效。传入的第一个参数代表函数名,第二个参数到最后一个参数会是这里作为参数传入函数中shift1是将传入脚本的参数向左移动一位,例如:传入脚本参数有三个参数$1$2$3,向左移动一位后,$2移动到$1的位置,$3移动到$2的位置,参数个数发生变化原因2:传入脚本的参数中,第一个参数是函数名,第二个参数是函数的参数。如果不左移,第一个参数的函数名也会作为参数传入下面的函数是完整的脚本内容a.shscript#!/bin/bashVAR_A=10test_a(){echo"test_a..pid:$$,p1:$1,p2:$2"}get_var(){echo${VAR_A}}if[$#-ge1];thenname="$1"shift1$name"$@"fib.sh脚本#!/bin/bashecho"b.sh...pid:$$"sha.shtest_a35ret=$(sha.shget_var)执行./b.sh命令,结果如下[root@ecs-centos-7~]#./b.shb.sh...pid:25086test_a..pid:25087,p1:3,p2:5ret:10可以可以看到结果和上面案例的方法是一样的。现在其他脚本可以通过sha.sh函数名参数列表调用a.sh脚本中的函数。通过$(sha.sh函数名参数列表)方法获取a.sh脚本函数的返回值。与case分支选择的方式相比,函数调用模板的优势在于调用者只需要关心复用脚本中的函数名和函数入参即可,函数的返回值可以直接使用。缺点是如果多个脚本调用复用脚本中的函数,当复用脚本中的函数名发生变化时,需要修改所有调用的地方。函数调用模板法的缺点就是有点case分支选择法。当使用case分支选择方式时,根据传入的字符串参数调用不同的函数。这里的字符串参数相当于函数的别名。只要这个参数不变,脚本中的函数名就可以任意改变上面的优缺点比较只是相对比较,在实际应用中不会很明显。在大多数情况下,这两种方法都可以使用。总结在编写shell脚本的过程中,经常会遇到一些莫名其妙的问题。有些问题挠头也不知道怎么解决。脚本复用可以将一些公共函数抽取出来,一个一个地形成功能模块,这样不仅有助于减少我们在编写脚本时犯的错误,也便于后期的脚本维护。有帮助