bash相当于Python的"if __name__ == '__main__'"是什么?



在Bash中,我希望能够同时源代码和执行文件。Bash与Python的if __name__ == '__main__'等价于什么

我在Stackoverflow上没有找到关于这个主题的现成问题/解决方案(我怀疑我的提问方式与现有的问题/答案不匹配,但由于我的Python经验,这是我能想到的最明显的表达方式)。


p.s.关于可能重复的问题(如果我有更多的时间,我会写一个更短的回复):

链接到的问题问"如何检测脚本是否来源",但这个问题问"你如何创建一个既可以来源又可以作为脚本运行的bash脚本?"。这个问题的答案可能使用前一个问题的某些方面,但有以下额外要求/问题:

  • 一旦检测到脚本的来源,最好的方法是不运行脚本(并避免意外的副作用(除了导入感兴趣的函数),如添加/删除/修改环境/变量)
  • 一旦你检测到脚本正在运行而不是源代码,实现脚本的规范方式是什么(把它放在函数中?或者可能只是放在if语句之后?如果你把它放到if语句之后,它会有副作用吗
  • 我在Bash上发现的大多数谷歌搜索都没有涵盖这个主题(一个既可以来源又可以执行的Bash脚本)实现这一点的规范方法是什么?这个话题是因为不鼓励还是不好做而没有被涵盖?有gotchas吗

解决方案:

if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi

我添加这个答案是因为我想要一个以模仿Python的if __name__ == '__main__'但使用Bash的风格编写的答案。

关于BASH_SOURCE$_的用法。我使用BASH_SOURCE是因为它看起来比$_(链接1,链接2)更健壮。


下面是我用两个Bash脚本测试/验证的一个示例。

xyz()函数的script1.sh:

#!/bin/bash
xyz() {
echo "Entering script1's xyz()"
}
main() {
xyz
echo "Entering script1's main()"
}
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi

script2.sh尝试调用函数xyz():

#!/bin/bash
source script1.sh
xyz    

使用FUNCNAME

FUNCNAME是一个仅在函数中可用的数组,其中FUNCNAME[0]是函数的名称,FUNCNAME[1]是调用方的名称。因此,对于顶级函数,在执行的脚本中FUNCNAME[1]将是main,或者在源脚本中是source

#!/bin/bash
get_funcname_1(){
printf '%sn' "${FUNCNAME[1]}"
}
_main(){
echo hello
}
if [[ $(get_funcname_1) == main ]]; then
_main
fi

示例运行:

$ bash funcname_test.sh 
hello
$ source funcname_test.sh 
$ _main
hello

我不知道我是怎么偶然发现的。manbash没有提到source值。

替代方法:在函数外使用FUNCNAME[0]

这只适用于4.3和4.4,没有记录。

if [[ ${FUNCNAME[0]} == main ]]; then
_main
fi

没有。我通常使用这个:

#!/bin/bash
main()
{
# validate parameters
echo "In main: $@"
# your code here
}
main "$@"

如果你想知道这个脚本是否是source'd,只需将你的main调用包装在中即可

if [[ "$_" != "$0" ]]; then
echo "Script is being sourced, not calling main()" 
else
echo "Script is a subshell, calling main()"
main "$@"
fi

参考:如何检测脚本是否来源于

return 2> /dev/null

要获得一种惯用的Bash方法,可以使用return,如下所示:

_main(){
echo hello
}
# End sourced section
return 2> /dev/null
_main

示例运行:

$ bash return_test.sh 
hello
$ source return_test.sh 
$ _main
hello

如果脚本是来源的,return将返回到父级(当然),但如果脚本被执行,return将产生一个隐藏的错误,脚本将继续执行。

我已经在GNUBash 4.2到5.0上测试过了,这是我的首选解决方案。

警告:这在大多数其他shell中都不起作用。

这是基于mr.spuratic关于如何检测脚本是否来源的部分答案

我真的认为这是实现Bash:中等效if __name__ == "__main__"的最Python和最漂亮的方法

来自我的eRCaGuy_hello_world repo:中的hello_world_best.sh

#!/usr/bin/env bash
main() {
echo "Running main."
# Add your main function code here
}
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
# This script is being run.
__name__="__main__"
else
# This script is being sourced.
__name__="__source__"
fi
# Only run `main` if this script is being **run**, NOT sourced (imported)
if [ "$__name__" = "__main__" ]; then
main "$@"
fi

要运行它,请运行./hello_world_best.sh。以下是运行和输出示例:

eRCaGuy_hello_world/bash$ ./hello_world_best.sh 
Running main.

要获取(导入)它,请运行. ./hello_world_best.sh. hello_world_best.shsource hello_world_best.sh等。当您获取它时,在这种情况下不会看到任何输出,但它会允许您访问其中的函数,因此您可以手动调用main。如果你想了解更多关于";采购";意思是,请看我的答案:Unix:源代码和导出之间的区别是什么?。以下是一个示例运行和输出,包括在获取文件后手动调用main函数:

eRCaGuy_hello_world/bash$ . hello_world_best.sh 
eRCaGuy_hello_world/bash$ main
Running main.

重要信息:

我曾经认为使用基于"${FUNCNAME[-1]}"的技术更好,但事实并非如此!事实证明,该技术并不能优雅地处理嵌套脚本,其中一个脚本调用或源于另一个脚本。要做到这一点,您必须1)将"${FUNCNAME[-1]}"放入函数中,2)根据嵌套脚本的级别将索引更改为FUNCNAMEarrray,这是不实际的。因此,不要使用我以前推荐的"${FUNCNAME[-1]}"技术。相反,使用我现在推荐的if [ "${BASH_SOURCE[0]}" = "$0" ]技术,因为它可以完美地处理嵌套脚本!

更进一步

  1. 我刚刚添加了一个非常详细的示例:如何在Bash中编写、导入和使用库?,它使用了我上面的技术。我还展示了Python中相对导入的详细示例,这样它们就可以在克隆到任何人计算机上的repo中自动工作,而无需重新配置任何内容

参考文献

  1. 这里的主要答案是我第一次了解"${BASH_SOURCE[0]}" = "$0"的地方
  2. 我第一次了解${FUNCNAME[-1]}技巧的地方是:@mr.spuratic:如何检测脚本是否来源——他显然是从Dennis Williamson那里学到的
  3. 在过去的一年里,我们进行了大量的个人学习、尝试和努力
  4. 我的代码:eRCaGuy_hello_world/bash/if__name__==__main___check_if_sourced_or_executed_best.sh
  5. 我的另一个相关但较长的答案:如何检测脚本是否来源

我一直在所有脚本的底部使用以下构造:

[[ "$(caller)" != "0 "* ]] || main "$@"

脚本中的所有其他内容都是在函数中定义的,或者都是全局变量。

caller被记录为"返回当前子例程调用的上下文"。当脚本被来源时,调用方的结果从来源该脚本的行号开始。如果该脚本不是源代码,则以"0 "开始

我使用!=||而不是=&&的原因是后者会导致脚本在来源时返回false。如果外部脚本在set -e下运行,这可能会导致它退出。

注意,我只知道这适用于bash。它不适用于posix外壳。我不知道其他shell,比如ksh或zsh。

相关内容

  • 没有找到相关文章

最新更新