你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

shell脚本中如何执行外部脚本

[复制链接]
gaosmile 发布时间:2020-11-14 14:25
在Linux开发中,经常会编写shell脚本来执行一些任务,通常是一个脚本只做一件事,随着任务的增加,脚本会越来越多,可复用的地方也会逐渐增加,这时就需要提取出脚本中的公共的功能放到一个通用的脚本中,其他脚本都能复用它
本篇文章介绍shell脚本中如何执行外部脚本,如何调用外部脚本中的函数,以及脚本复用相关的方法
执行外部脚本的方式
假如在当前目录有 a.sh 脚本,内容如下
#!/bin/bash

echo "a.sh..."
在一个脚本中执行外部脚本主要有以下几种方式
  • source 外部脚本名字

在当前目录下的 b.sh 脚本,内容如下:
#!/bin/bash

source a.sh
echo "b.sh..."
执行 ./b.sh,结果如下
[root@ecs-centos-7 ~]# ./b.sh
a.sh...
b.sh...
脚本中 source a.sh 命令 会先执行当前目录下的 a.sh脚本,所以结果会先输出 a.sh...再输出 b.sh脚本本身的打印
  • 点号 外部脚本名字

把 b.sh 脚本中执行a.sh脚本的语句修改成 点号 + 空格 + a.sh ,修改之后的脚本内容如下:
注意:点号和a.sh之间一定要加上空格,否则执行的时候会出错
#!/bin/bash

. a.sh
echo "b.sh..."
执行 ./b.sh,结果如下
[root@ecs-centos-7 ~]# ./b.sh
a.sh...
b.sh...
在上述脚本中, . a.sh 会先执行a.sh脚本, 结果会先输出 a.sh...再输出 b.sh...
  • sh 外部脚本名字

sh 外部脚本名字 和 ./外部脚本名字 两种方式是一样的,选择哪一种方式都没问题,下面是以前面一种方式为例说明的
把 b.sh 脚本中 source a.sh修改成 sh a.sh ,修改之后的脚本内容如下:
#!/bin/bash

sh a.sh
echo "b.sh..."
执行 ./b.sh 命令, 结果如下
[root@ecs-centos-7 ~]# ./b.sh
a.sh...
b.sh...
可以看出,结果输出和上面两种方式是一样的
三种方式的有什么区别
调用外部脚本有 source 外部脚本 、点号 外部脚本、sh 外部脚本 三种方式,它们之间有什么区别呢?
其中,source 外部脚本 和 点号 外部脚本 两种方式是相同的,当前脚本继承了外部脚本的全局变量和函数, 相当于把外部脚本的函数和全局变量导入了当前脚本中
修改 a.sh 和 b.sh 脚本, 内容如下
a.sh脚本
#!/bin/bash

VAR_A=10

func_a()
{
  echo "a.sh...pid$,param1"
}
b.sh脚本
#!/bin/bash

source a.sh

func_a $1
echo "varaVAR_A"
echo "b.sh...pid$"
执行 ./b.sh 5 命令,结果如下
[root@ecs-centos-7 ~]# ./b.sh 5
a.sh...pid:21485,param:5
vara:10
b.sh...pid:21485
两个脚本中的 $$ 是指执行脚本的进程ID,从结果可以看出,a.sh 和 b.sh 都是在同一个进程内执行的,所以在 b.sh 脚本中执行 source a.sh 命令,会把 a.sh 脚本中的全局变量 VAR_A 和函数 func_a导入到 b.sh中
在 b.sh中打印变量 VAR_A,输出的值和 a.sh中相同,调用 func_a函数,输出也说明了调用的是 a.sh中的函数
source 外部脚本 、点号 外部脚本 两种方式是相同的, 所以, 把 b.sh 中 source a.sh 修改成 . a.sh , 执行 ./b.sh 5, 结果依然是相同的
由于 sh 外部脚本的方式是当前脚本和外部脚本在两个不同的进程中执行,所以当前脚本不能直接使用外部脚本中的函数和全局变量
修改 a.sh 和 b.sh 脚本, 内容如下
a.sh脚本
#!/bin/bash

test_a()
{
  echo "a.sh...test_a"
}

echo "a.sh...pid$"
b.sh脚本
#!/bin/bash

sh a.sh

echo "b.sh...pid$"

test_a
执行 ./b.sh 命令,结果如下
[root@ecs-centos-7 ~]# ./b.sh
a.sh...pid:21818
b.sh...pid:21817
./b.sh:行7: test_a: 未找到命令
从结果可以看出,执行 a.sh 和 b.sh 的进程ID是不同的,b.sh脚本进程找不到test_a函数,所以在b.sh中调用test_a 函数会提示 未找到命令
调用外部脚本中的函数
上一节讲到 sh 外部脚本 的方式无法直接使用外部脚本中函数和全局变量,下面提供几种方法可以解决这个问题
  • case 分支选择

这种方法类似于程序代码中的 switch case 语句,通过switch 选择不同的分支从而执行不同的逻辑,shell脚本中是使用case关键字来实现的
a.sh脚本
#!/bin/bash

VAR_A=10

test_a()
{
   echo "test_a..pid$,p11,p22"
}
get_var()
{
  echo ${VAR_A}
}

case "$1" in
    ta)
      test_a $2 $3
      ;;
    var)
      get_var
      ;;
   *)
      echo "parameter err..."
esac
b.sh脚本
#!/bin/bash

echo "b.sh...pid$"

sh a.sh ta 3 5

ret=$(sh a.sh var)

echo "ret:$ret"
执行 ./b.sh 命令,结果如下
[root@ecs-centos-7 ~]# ./b.sh
b.sh...pid:24813
test_a..pid:24814,p1:3,p2:5
ret:10
脚本b.sh一开始打印了调用自身的进程ID
sh a.sh ta 3 5 语句是调用a.sh脚本,传入的三个参数分别是ta, 3, 5 ,执行a.sh时,传入的第一个参数 ta经过case匹配之后调用 test_a函数,并把剩下的两个参数 3和5作为参数传入函数
ret=$(sh a.sh var) 语句时调用a.sh脚本,传入一个var 参数,经过case匹配之后调用get_var函数,该函数的作用输出脚本中全局变量VAR_A的值,语句中$()的作用是获取()中命令的返回值,这里是把a.sh脚本中 get_var函数的返回值赋值给 ret变量,所以该变量的值是 a.sh脚本中全局变量VAR_A的值
说明:如果想要获取函数的返回值,可以在函数中用 echo 打印相应的输出值,然后使用$(函数名 参数列表)可以获取到函数中打印的值,如上面b.sh脚本中 ret=$(sh a.sh var)语句,变量ret的值是 a.sh脚本中 get_var函数输出的值10
这里需要注意的是, 如果函数中有echo调试日志,那么调试日志也会一起返回
  • 函数调用模板

上面介绍的用 case 关键字去匹配调用不同的函数有一个缺点,每次a.sh脚本中增加一个函数的时候,case 就需要添加一个分支,分支里调用不同的函数,还需要注意函数是否有参数传入以及参数数量是否正确
我们可以在每个供外部调用脚本的尾部加上以下的语句,就可以解决上述问题, 具体语句如下
if [ $# -ge 1 ]; then
   name="$1"
   shift 1
   $name "$@"
fi
上述语句首先判断调用脚本时传入的参数数量,只有参数数量大于等于1才有效,传入的第一个参数表示函数名字,从第二个参数到最后一个参数都会作为参数传入到函数中
这里的 shift 1 是把传入脚本的参数左移一个位置,比如:传入脚本参数有 $1 $2 $3三个参数,左移一个位置之后, $2 移动到 $1 的位置,$3 移动到 $2 的位置,参数数量变为2了
原因: 传入脚本的参数中,第一个参数是函数名字,从第二个参数起才是函数的参数,如果不做左移处理,第一个参数函数名字也会作为参数传入到函数中
下面是完整的脚本内容
a.sh脚本
#!/bin/bash

VAR_A=10

test_a()
{
   echo "test_a..pid:$$,p1:$1,p2:$2"
}

get_var()
{
  echo ${VAR_A}
}

if [ $# -ge 1 ]; then
   name="$1"
   shift 1
   $name "$@"
fi
b.sh脚本
#!/bin/bash

echo "b.sh...pid:$$"

sh a.sh test_a 3 5

ret=$(sh a.sh get_var)
执行 ./b.sh 命令,结果如下
[root@ecs-centos-7 ~]# ./b.sh
b.sh...pid:25086
test_a..pid:25087,p1:3,p2:5
ret:10
可以看出,结果和上面 case 的方法是一样的
现在其他脚本中都可以通过 sh a.sh 函数名 参数列表 这样的方式调用 a.sh 脚本中的函数了,通过 $(sh a.sh 函数名 参数列表)的方式获取 a.sh脚本函数的返回值
  • 两者的优缺点

与case分支选择的方式相比,函数调用模板的优点是调用者只需要关心复用的脚本中函数名、函数传入参数、函数返回值就可以直接使用
缺点是如果有多个脚本都调用了复用脚本中的函数,当复用脚本中函数名变更时,需要修改所有调用了它的地方
函数调用模板方式的缺点恰恰是case分支选择方式的有点,case分支选择的方式时根据传入的字符串参数调用不同的函数,这里的字符串参数相当于函数的别名,只要这个参数保持不变,脚本中的函数名字可以任意变更
上述的优缺点比较只是一个相对的比较,实际应用中下不会很明显,大部分情况两种方式都可以使用
小结
在编写shell脚本的过程中,经常会遇到一些莫名奇妙的问题,有些问题就算挠破头皮都不知道如何解决,脚本复用可以把一些公共功能提取出来,形成一个个的功能模块,不仅有助于减少我们编写脚本时犯的错误,而且对后期的脚本维护很有帮助

收藏 评论0 发布时间:2020-11-14 14:25

举报

0个回答

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版