【问题标题】:What is the bash equivalent to Python's `if __name__ == '__main__'`?bash 等效于 Python 的 `if __name__ == '__main__'` 是什么?
【发布时间】:2015-04-30 11:24:46
【问题描述】:

在 Bash 中,我希望能够获取脚本并执行文件。 Bash 相当于 Python 的 if __name__ == '__main__' 是什么?

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


附言关于possible duplicate question(如果我有更多时间,我会写一个更短的回复):

linked to question 询问“如何检测是否正在获取脚本”,但该问题询问“如何创建既可以作为脚本获取又可以作为脚本运行的 bash 脚本?”。这个问题的答案可能会用到上一个问题的某些方面,但有如下额外的要求/问题:

  • 一旦您检测到正在获取脚本,不运行脚本的最佳方法是什么(并避免意外的side-effects(除了导入感兴趣的函数),例如添加/删除/修改环境/变量)李>
  • 一旦您检测到脚本正在运行而不是源代码,实现脚本的规范方式是什么(将其放在函数中?或者只是将其放在 if 语句之后?如果您将它放在 if 语句之后有副作用吗?
  • 我在 Bash 上找到的大多数 google 搜索都没有涵盖这个主题(一个可以获取和执行的 bash 脚本)什么是实现这个的规范方法?是否因为不鼓励或不好做而没有涵盖该主题?有问题吗?

【问题讨论】:

  • 不精通 Python,我不明白你的问题。 :) 您是否希望能够检测脚本是显式运行,还是通过使用source 或点运算符包含在另一个脚本中?
  • @ghoti 例如,我有一个脚本script1.sh 和一个函数xyz(),我想在另一个脚本script2.sh 中使用它。当我从 script2 获取 script1 时,script1 以某种方式退出,阻止我在 script2 中调用 xyz()。 (我提到的python位的一个很好的解释是here

标签: bash


【解决方案1】:

解决方案:

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

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

关于BASH_SOURCE$_ 的用法。我使用BASH_SOURCE,因为它似乎比$_link1link2)更强大。


这是我使用两个 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

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

#!/bin/bash

source script1.sh

xyz    

【讨论】:

  • $0 本身就是 not entirely reliable 所以我不确定这是否一定会起作用。
  • @EtanReisner 如果您认为自己有更好的解决方案,请随意编写自己的答案。 (此时有 60 多个人赞成使用 $0 解决两个不同的问题(herehere)的答案
  • 我没有。我只是想指出$0 是不可靠的,这可能会导致问题(但在大多数情况下可能不会)。
  • ${BASH_SOURCE[0]}igncr${0} 更适合:
【解决方案2】:

使用FUNCNAME

方法一:测试FUNCNAME[0](Bash 4.3+)

#!/bin/bash

_main(){
    echo hello
}

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

示例运行:

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

我不知道我是怎么偶然发现的。 man bash 没有很好地描述它的功能,但它实际上是这样做的:

  • 执行的脚本:${FUNCNAME[0]}main
  • 来源脚本:${FUNCNAME[0]}source
  • Shell 函数:${FUNCNAME[0]} 是函数名

方法2:在函数内测试FUNCNAME[1] (Bash 4.2)

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

这个例子应该和上面的例子一样工作。

#!/bin/bash

get_funcname_1(){
    printf '%s\n' "${FUNCNAME[1]}"
}

_main(){
    echo hello
}

if [[ $(get_funcname_1) == main ]]; then
    _main
fi

【讨论】:

  • 根据 bash 5.2 的手册并通过测试验证,FUNCNAME 再次仅在执行的 shell 函数中定义。
  • @MJB 真的!我在 Bash 5.0 中注意到了同样的事情。我会尽快更新答案。
【解决方案3】:

没有。我通常用这个:

#!/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

参考:How to detect if a script is being sourced

【讨论】:

    【解决方案4】:

    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 将产生一个隐藏的错误,脚本将继续执行。

    我已经在 GNU Bash 4.2 到 4.4 上对此进行了测试

    这是基于mr.spuratic's answerHow to detect if a script is being sourced 上的部分内容。

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

    【讨论】:

      【解决方案5】:

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

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

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

      caller 记录为“返回当前子例程调用的上下文”。获取脚本时,调用者的结果以获取该脚本的脚本的行号开头。如果这个脚本没有被获取,它以"0 "开始

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

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

      【讨论】:

        【解决方案6】:

        我最喜欢的前 2 个是:

        1. [我的第一个最爱](必须在内部函数): 修改自:How to detect if a script is being sourced
          https://unix.stackexchange.com/questions/424492/how-to-define-a-shell-script-to-be-sourced-not-run/424552#424552
          并在这里以稍微不同的形式提到:What is the bash equivalent to Python's `if __name__ == '__main__'`?
          if [ "${FUNCNAME[-1]}" == "main" ]; then
              echo "  This script is being EXECUTED."
              run="true"
          elif [ "${FUNCNAME[-1]}" == "source" ]; then
              echo "  This script is being SOURCED."
          else
              echo "  ERROR: THIS TECHNIQUE IS BROKEN"
          fi
          
        2. [我的第二个收藏夹](可以放在任何地方): 修改自:What is the bash equivalent to Python's `if __name__ == '__main__'`?
          if [ "${BASH_SOURCE[0]}" == "$0" ]; then
              echo "  This script is being EXECUTED."
              run="true"
          else
              echo "  This script is being SOURCED."
          fi
          

        在此处查看我在其他答案中编译的所有 4 种技术:How to detect if a script is being sourced

        【讨论】:

          猜你喜欢
          • 2011-06-09
          • 1970-01-01
          • 2010-11-01
          • 2016-04-22
          • 2011-01-15
          • 2015-11-13
          • 2018-11-22
          相关资源
          最近更新 更多