深入理解 Shell 脚本中的 `$0` 与 `${BASH_SOURCE[0]}`
在Shell脚本的编写中,经常会遇到需要引用脚本本身的位置或名称的场景。$0 和 ${BASH_SOURCE[0]} 就是用于表示当前运行脚本信息的两个变量,但它们之间存在一些差异。在今天的博客中,我们将详细探讨这两个变量的区别,并了解在什么情况下该使用哪一个。
什么是 $0?
$0 是一个特殊变量,在 Bash 或其他Unix shell中被用来引用当前执行的脚本或命令的名字。这个变量的一个常见用途是在脚本中获取脚本自身的名字。
例如:
#!/bin/bash
echo "The name of this script is '$0'"
如果你将这个脚本保存为 my_script.sh 并执行它,你会看到输出 The name of this script is 'my_script.sh'。然而,当你通过一个链接或者通过某些路径来调用这个脚本时,输出可能会包含这些额外的路径信息。
什么是 ${BASH_SOURCE[0]}?
相对与 $0,${BASH_SOURCE[0]} 是一个相对较新的特性,它仅在 Bash 脚本中可用。这个变量被用来获取正在执行的 Bash 脚本的文件名。相比 $0,${BASH_SOURCE[0]} 提供了一种更可靠的方式来获取脚本的路径,特别是在脚本被 source 命令执行或者在函数和别的脚本内被调用时。
例如:
#!/bin/bash
function script_name {
echo "The name of this script is '${BASH_SOURCE[0]}'"
}
script_name
无论这个脚本如何被调用,${BASH_SOURCE[0]} 都会准确地返回脚本文件的名字。
$0 与 ${BASH_SOURCE[0]} 的区别
让我们通过一个例子来揭示这两个变量之间的差异:
假设我们有一个名为 caller.sh 的脚本,它调用了另一个名为 script.sh 的脚本,而 script.sh 包含了一个函数,此函数中既打印 $0 又打印 ${BASH_SOURCE[0]} 的值。
caller.sh 脚本内容:
#!/bin/bash
# 调用另一个脚本
./script.sh
script.sh 脚本内容:
#!/bin/bash
function print_script_name {
echo "\$0 is $0"
echo "\${BASH_SOURCE[0]} is ${BASH_SOURCE[0]}"
}
print_script_name
当你执行 caller.sh 时,你可能会看到如下输出:
$0 is ./caller.sh
${BASH_SOURCE[0]} is ./script.sh
在这个例子中:
$0会打印出调用script.sh的脚本名caller.sh。${BASH_SOURCE[0]}会打印出当前执行的脚本文件名script.sh。
这一区别显得尤为重要,因为当你需要知道当前运行脚本的确切位置而非调用他的脚本时,你应该使用 ${BASH_SOURCE[0]}。
${BASH_SOURCE[1]} 的用途
在函数或脚本的调用堆栈中,${BASH_SOURCE[1]} 可以用来获取调用当前脚本或函数的脚本文件名。在有多个脚本层次调用的复杂情况下,这个变量帮助你追踪脚本的调用来源。
继续上面的例子,如果在 caller.sh 中 source 了 script.sh 而不是执行它:
caller.sh 脚本内容变更如下:
#!/bin/bash
# 调用另一个脚本
source ./script.sh
print_caller_name
那么在 script.sh 中加入以下函数:
function print_caller_name {
echo "${BASH_SOURCE[1]} is the caller script."
}
当执行 caller.sh 脚本时,将会打印:
caller.sh is the caller script.
${BASH_SOURCE[@]} 和调用栈
实际上,BASH_SOURCE 是一个数组,包含了当前命令或函数调用栈的脚本和源文件名的列表。数组中的第一个元素 ${BASH_SOURCE[0]} 是当前执行的脚本或源文件,第二个元素 ${BASH_SOURCE[1]} 是调用这个脚本或函数的父脚本,以此类推。
了解和利用这些信息,可以帮助脚本编写者更好地定位问题和调试。特别地,在编写库函数和需要追踪调用关系的时候,这通常是不可或缺的工具。利用该特性可以编写更加健壮的、能够适应不同使用情况的 bash 脚本。
应用
在 Python 中,当一个模块被直接运行时,__name__ 变量被设置为 "__main__"。这一特性常用于写在 Python 模块最下方的以下代码:
if __name__ == "__main__":
main()
这样,当模块被直接运行时(而不是被导入到另一个模块中时),代码会执行 main() 函数或者模块中的特定代码块。
在 Bash 脚本中,使用 if [[ $0 == ${BASH_SOURCE[0]} ]]; then 的模式功能上与 Python 中的 if __name__ == "__main__": 语句相似。这个 Shell 脚本的构造在如下两种情境中判断当前脚本是被直接执行还是被 source:
- 脚本被直接执行(如
./script.sh):此时$0与${BASH_SOURCE[0]}相同,都是脚本的名称,因此这个条件判断成立,main函数或其他指定的代码将会执行。 - 脚本被 source (如
source script.sh或. script.sh):此时$0通常不等于脚本的名称,而是保持调用 shell 的名称,所以条件判断不成立。
通过这种方式,Bash 脚本可以模仿 Python 的行为,根据脚本是被直接调用还是被 source 来执行不同的代码逻辑。这允许你编写既可以作为可执行脚本运行,也可以被 source 以提供函数和变量设置给其他脚本的 Bash 脚本。
结论
简而言之,$0 和 ${BASH_SOURCE[0]} 都可用于获取脚本的名称,但是它们在特定场景下提供的信息是不同的。在单个脚本中执行时,两者可能显示相同的信息。然而,如果你需要在函数内、被 source 的脚本中或者脚本链的任意点确定当前脚本的准确位置,${BASH_SOURCE[0]} 是更可靠的选择。
记住检查你的脚本是否需要在不同的环境和上下文中准确地知道它们自身的位置,然后选择合适的变量来使用。对于现代 Bash 脚本编程,更推荐使用 ${BASH_SOURCE[0]} 来获取脚本准确路径,而 $0 则用于获得被调用的命令或脚本名。

浙公网安备 33010602011771号