本文介绍了如何在Linuxbashshell脚本中执行外部脚本文件,以及如何调用其他脚本中的函数以重用外部脚本代码。在shell脚本中执行外部脚本文件在bashshell脚本文件中,如果需要执行其他外部脚本文件,类似于执行外部命令,可以直接通过外部脚本文件名来执行,可能需要提供一个寻址路径。比如在a.sh脚本文件中执行当前目录下的b.sh脚本文件,直接在a.sh文件中写入./b.sh语句即可执行b.sh脚本文件。如果需要提供参数,把参数写在./b.sh后面即可。不同的参数用空格分隔。如果参数本身包含空格,请将参数括在引号中。例如写./b.sharg1arg2。当执行a.sh脚本文件时,a.sh将在子shell中运行。这个子shell启动另一个子shell来执行b.sh脚本文件。在b.sh中执行exit命令只会退出b.sh的执行,不会退出a.sh的执行。注意:在a.sh脚本中执行b.sh脚本文件时,如果b.sh脚本文件没有放在PATH环境变量可以寻址的目录下,那么需要提供b.sh脚本a.sh地址路径中的文件。建议写成绝对路径,保证b.sh脚本文件总能寻址到。如果写成相对路径,会受到执行a.sh的工作目录的影响。根据给定的相对路径,可能找不到b.sh脚本文件。假设有一个脚本文件a.sh,其内容如下#!/bin/bashecho"$0:thisisa.sh"./b.sh有一个脚本文件b.sh,其内容如下:#!/bin/bashecho"$0:thisisa.sh"./b.sh/bin/bashecho"$0:thisisb.sh"把a.sh和b.sh放在同一个目录下,都加上可执行权限。在与两个脚本文件相同的目录中开始测试:$./a.sh./a.sh:thisisa.sh./b.sh:thisisb.sh$mkdirsubdir$cdsubdir$../a.sh../a.sh:thisisa.sh../a.sh:line4:./b.sh:Nosuchfileordirectory可以看到,在这两个脚本文件所在的目录下位于,执行./a.sh,可以正常寻址b.sh,执行b.sh。但是进入该目录下的subdir目录,通过../a.sh执行父目录的a.sh脚本文件,会提示找不到./b.sh脚本文件,b.sh不能执行。即在a.sh脚本中执行b.sh脚本时,b.sh是根据当前工作目录寻址的,而不是根据a.sh脚本文件所在目录寻址的。当前工作目录可能不固定,a.sh写成地址b.sh的相对路径。后续执行可能会导致找不到b.sh。如果写成绝对路径,则不受工作目录的影响。使用source命令执行其他脚本在bash中,您可以使用source内置命令或.内置命令执行当前脚本进程中的其他脚本文件。源命令和.命令是等价的。查看helpsource的帮助信息与help.的帮助信息完全一样。后面会以source命令为例进行统一说明。有关说明,请参阅帮助源:sourcefilename[arguments]从当前shell中的文件执行命令。在当前shell中从FILENAME读取和执行命令。$PATH中的条目用于查找包含FILENAME的目录。如果提供了任何ARGUMENTS,它们将成为执行FILENAME时的位置参数。退出状态:返回FILENAME中最后执行的命令的状态;如果无法读取FILENAME,则失败。即bash在执行一个脚本文件时,会默认启动一个子shell来执行脚本在一个子进程下运行。通过source命令执行脚本文件时,直接在当前shell下运行。提供的参数将用作执行脚本文件的位置参数。在a.sh脚本文件中,通过source命令执行b.sh脚本文件。一个好处是b.sh中的函数可以通过a.sh中的函数名单独调用。这有助于shell脚本的代码重用。假设有一个包含以下内容的testsource.sh脚本文件:#!/bin/bashecho"$0:thisisa.sh"./utils.shutils_printsource./utils.shutils_print有一个包含以下内容的utils.sh脚本文件:#!/bin/bashutils_print(){echo-e"b.sh:thisisutils_print"}将这两个脚本文件放在同一个目录下,执行testsource.sh脚本文件,结果如下:$./测试源。sh./testsource.sh:thisisa.sh./testsource.sh:line5:utils_print:commandnotfoundb.sh:thisisutils_print可以看到,在testsource.sh中执行./utils.sh语句后,执行utils_print报错,提示找不到命令。该方法不能通过函数名调用外部脚本文件中的函数。在testsource.sh中执行source./utils.sh语句后,执行utils_print没有报错,确实调用了utils.sh中的函数。即通过source命令执行外部脚本后,会将执行脚本的全局变量名和函数名导出到当前shell中。外部脚本中的函数可以通过函数名来调用,外部脚本中的全局变量也可以通过变量名来引用。注意:假设a.sh脚本通过source命令执行b.sh脚本,在实际使用中有一些需要注意的地方。具体说明如下:由于source命令执行的脚本文件会在当前shell下运行,如果b.sh脚本执行exit语句,不仅脚本会退出,调用的a.sh脚本也会退出它也会退出。如果不希望b.sh脚本的执行状态影响a.sh脚本的执行状态,那么b.sh脚本要谨慎使用exit语句。a.sh脚本中,b.sh脚本必须通过绝对路径寻址,或者将b.sh脚本文件放在PATH环境变量可以寻址的目录下,这样才能保证当a.sh脚本在任意工作目录下执行,可寻址b.sh脚本。在a.sh脚本中,b.sh脚本是通过source命令执行的,那么b.sh脚本代码中的$#是对应a.sh的参数个数,还是b.sh时的参数个数.sh脚本被执行?答案是视情况而定。说明如下:如果a.sh脚本没有给b.sh脚本传递参数,则获取b.sh脚本中$#的值,对应传递给a.sh的参数个数。比如执行source./b.sh,那么b.sh打印出来的$#的值就等于a.sh打印出来的值。如果a.sh脚本传递参数给b.sh脚本,那么b.sh脚本中的$#对应传递给b.sh的参数个数。例如执行source./b.sh123,则b.sh打印的$#的值为3。使用source命令执行b.sh脚本,b.sh的全局变量脚本将导出到当前shell。这个全局变量的值将一直存在,直到当前shell退出。即使b.sh脚本执行结束,里面的全局变量值依然存在。使用source命令再次调用b.sh。如果不重新分配全局变量,它的值将不会改变。将之前的utils.sh脚本修改为如下内容:#!/bin/bashdeclarevalueif[$#-eq1];然后value="$1"fiecho$value这里使用declarevalue来声明一个值变量,但是没有给它赋初值。使用source命令多次执行脚本,结果如??下:$source./utils.shinit_valueinit_value$source./utils.shinit_value执行source./utils.shinit_value命令,以及utils.sh脚本将value变量分配给"init_value",并打印出该值。然后执行source./utils.sh命令,没有传递任何参数,没有给value变量赋值,但是打印出来的value变量的值是“init_value”,仍然是之前的值。即通过source命令多次执行一个脚本时,如果脚本使用了全局变量,需要根据实际需要确认是否需要给全局变量赋初值,避免干扰从以前保存的值。可以通过函数名调用内部函数的脚本模板。如上所述,通过source命令执行外部脚本后,可以通过函数名单独调用外部脚本中的函数。由于脚本会在当前shell下运行,所以会影响当前shell。编写脚本代码需要注意的地方有很多,否则会引入一些意想不到的问题。其实,除了使用source命令执行外部脚本,还可以通过函数名调用脚本中的函数。您只需要调整脚本代码的编写方式即可。假设要在utils.sh脚本中调用一个函数,可以让脚本接收一个或多个参数,指定要调用的函数名,以及要传递给函数的参数。然后utils.sh脚本自己执行这个函数。调用脚本的格式类似如下:utils.shfunction_name[arguments]此时,当前shell会启动一个子shell来运行utils.sh脚本。这种格式和source命令的格式类似,只是source命令指定了要执行的脚本文件名,而这里指定了要调用的函数名。为了满足这个要求,修改utils.sh脚本如下:#!/bin/bashfunctionshow_args(){echo"Entershow_args():FUNCNAME:$FUNCNAME"echo"Entershow_args():\$1:$1"echo"参数是:$@"}if[$#-ne0];然后funcname="$1"shift1$funcname"$@"fi当$#不等于0时,表示提供了参数,那么第一个参数应该是要调用的函数名,存放在函数名变量。然后执行shift1命令将位置参数左移一个。原来的2会变成1,3会变成2,依此类推。这样做会跳过传递给脚本的第一个参数。这个参数是你要调用的函数名,不需要传递给被调用函数。最后执行$funcname"$@"语句,即调用funcname变量保存的函数,将移动后的位置参数传递给函数。这里实现了一个show_args函数,打印当前函数名,传入的第一个参数,以及所有的参数。具体测试结果如下:$./utils.shshow_args123输入show_args():FUNCNAME:show_args输入show_args():$1:1参数为:123$value=$(./utils.shshow_args123)$echo$valueEntershow_args():FUNCNAME:show_argsEntershow_args():$1:1参数是:123如你所见,./utils.shshow_args123命令确实调用了show_argsutils.sh脚本函数,并将123作为参数传递给该函数。如果上面没有执行shift1命令,那么show_args函数接收到的$1参数就是show_args,$2就是1。期望是函数的第一个参数是1,对应$1。执行shift1命令来达到这个效果。如果要获取函数输出的字符串,使用echo命令打印函数中对应的字符串,然后使用$()获取值。如上面的value=$(./utils.shshow_args123)命令所示。打印value变量的值,可以看到它保存了show_args函数输出的所有字符串。在这种情况下,函数中不能使用echo命令打印调试信息,否则调试信息也会被调用者获取到,影响调用者对字符串的解析。这段utils.sh脚本代码是一个脚本模板,可以通过函数名调用内部函数。根据实际需要添加相应的函数,这些函数可以复用,方便其他脚本调用。
