Shell—关于source,bash如何执行
通过对一个脚本问题的分析,发现了自己的一个知识误区,我想,有必要写篇博客总结一下。
关于source
source test.sh 与 . test.sh 二者用法相同。是读取脚本test.sh中的内容,依次在当前脚本中执行,且不会建立新的子shell去执行。被引用脚本 test.sh中的所有新建、改变变量的语句都会保存在当前shell里面,并被执行。
一般,在被引用脚本 test.sh中会封装一个公用的代码或是校验类的接口函数,供其他脚本调用。
尤其需要理解区分的是,source不同于 “./ test.sh” 和 “/bin/bash test.sh”,此二者有本质的区别,后者是在当前脚本中执行子shell,会产生新的进程来执行,而且在父shell中不能调用子shell中包含的函数接口。而source之后的文件内容已被加载到当前脚本中,就可以调用其内部定义的函数。
本文测试的 test.sh 脚本如下,作为被引用的脚本文件
#!/bin/bash
echo "Enter test.sh"
function func1()
{
echo "enter function 1"
}
function func2()
{
echo " $1: source $2 "
echo "Exit test.sh"
exit 0
}
echo "Exit test.sh ~~~"
当执行exec.sh脚本时,exec.sh 中执行 ./test.sh,结果如下
验证:在父shell中不能调用子shell中包含的函数接口
bash如何执行
通过分析源码,bash是把执行的shell中的内容存储到hash表。source作为内置命令,其实现也在bash源码中(builtins/source.def 内置方法的文件都在builtins目录),当bash执行到source命令时,会将引用的脚本文件内容读入一个缓冲区,保存于一段临时开辟的hash表中,然后通过调用函数push_scope(VC_BLTNENV, tempporary_env)
push到当前脚本的上下文中,在哪里引用,就在哪加入当前脚本的上下文。最后,bash依次往下执行当前脚本。
本文中测试的当前脚本 source.sh 如下:
#!/bin/bash
if [ $# -ne 1 ]
then
echo "Usage: $0 source_file"
fi
source_file=$1
echo "First source $source_file"
source $source_file
echo "Exec func1 function"
func1
echo "Second source $source_file"
. $source_file
echo "Exec func2 function"
func2 "$0 $source_file"
echo "Exit source.sh" # func2 中存在 exit 0, bash执行不到这里,因此不会打印
在命令行执行 ./source.sh test.sh 打印如下:
Bash执行过程如下:
从上图看得出来,每一次source,当前执行脚本中就会重新读取被引用脚本 test.sh的内容并执行,因此在实际写shell时,要避免多次source一个文件,此处是为了示例source执行的过程。
对于简单的命令,要更加理解到位,不然还是会犯一些低级的小错误。