我有一个脚本,我不希望它调用退出,如果它是来源。
我想检查是否$0 == bash,但这有问题,如果脚本是从另一个脚本,或者如果用户从不同的shell,如ksh源。
是否有一种可靠的方法来检测脚本是否被引用?
我有一个脚本,我不希望它调用退出,如果它是来源。
我想检查是否$0 == bash,但这有问题,如果脚本是从另一个脚本,或者如果用户从不同的shell,如ksh源。
是否有一种可靠的方法来检测脚本是否被引用?
当前回答
我遵循mklement0紧凑表达式。
这很整洁,但我注意到,当调用ksh时,它可能会失败:
/bin/ksh -c ./myscript.sh
(它认为它是源的,而不是因为它执行了一个子shell) 但是表达式可以检测到这一点:
/bin/ksh ./myscript.sh
此外,即使表达式是紧凑的,语法也不兼容所有shell。
因此,我以以下代码结束,它适用于bash,zsh,dash和ksh
SOURCED=0
if [ -n "$ZSH_EVAL_CONTEXT" ]; then
[[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && SOURCED=1
elif [ -n "$KSH_VERSION" ]; then
[[ "$(cd $(dirname -- $0) && pwd -P)/$(basename -- $0)" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ]] && SOURCED=1
elif [ -n "$BASH_VERSION" ]; then
[[ $0 != "$BASH_SOURCE" ]] && SOURCED=1
elif grep -q dash /proc/$$/cmdline; then
case $0 in *dash*) SOURCED=1 ;; esac
fi
请随意添加异国情调的贝壳支持:)
其他回答
我想对丹尼斯非常有用的回答提出一个小小的更正,让它更容易携带,我希望:
[ "$_" != "$0" ] && echo "Script is being sourced" || echo "Script is a subshell"
因为[[不被Debian POSIX兼容外壳识别(有些保留的IMHO), dash。同样,在shell中,可能需要使用引号来防止文件名中包含空格。
这似乎在Bash和Korn之间是可移植的:
[[ $_ != $0 ]] && echo "Script is being sourced" || echo "Script is a subshell"
与此类似的行或赋值语句如pathname="$_"(带有稍后的测试和操作)必须位于脚本的第一行或shebang之后的行(如果使用了shebang,则应该用于ksh,以便它在大多数情况下工作)。
检测Bash脚本是否正在执行或导入的最漂亮方法
我真的认为这是最美丽的方式:
从我的eRCaGuy_hello_world repo中的if__name__==__main__ check_if_sourced_or_executed_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
echo "This script is being run."
main
else
echo "This script is being sourced."
fi
引用:
关于上述技术的其他详细信息,请参见我在这里的另一个回答,包括显示运行输出:bash与Python的if __name__ == '__main__'等价于什么? 这个答案,我第一次了解到“${BASH_SOURCE[0]}”=“$0”
如果您愿意,还可以探索以下替代方案,但我更喜欢使用上面的代码块。
重要提示:使用“${FUNCNAME[-1]}”技术不能正确处理嵌套脚本,即一个脚本调用或来源另一个脚本,而if ["${BASH_SOURCE[0]}" = "$0"]技术可以。这是使用if ["${BASH_SOURCE[0]}" = "$0"]的另一个重要原因。
4种方法确定bash脚本是源脚本还是执行脚本
我已经阅读了关于这个问题和其他一些问题的一堆答案,并提出了4种我想要总结并放在一个地方的方法。
if __name__ == "__main__":
参见:如果__name__ == "__main__":会做什么?在Python中所做的事情。
You can see a full demonstration of all 4 techniques below in my check_if_sourced_or_executed.sh script in my eRCaGuy_hello_world repo. You can see one of the techniques in-use in my advanced bash program with help menu, argument parsing, main function, automatic execute vs source detection (akin to if __name__ == "__main__": in Python), etc, see my demo/template program in this list here. It is currently called argument_parsing__3_advanced__gen_prog_template.sh, but if that name changes in the future I'll update it in the list at the link just above
不管怎样,这里有4个Bash技术:
Technique 1 (can be placed anywhere; handles nested scripts): See: https://unix.stackexchange.com/questions/424492/how-to-define-a-shell-script-to-be-sourced-not-run/424495#424495 if [ "${BASH_SOURCE[0]}" -ef "$0" ]; then echo " This script is being EXECUTED." run="true" else echo " This script is being SOURCED." fi Technique 2 [My favorite technique] (can be placed anywhere; handles nestes scripts): See this type of technique in-use in my most-advanced bash demo script yet, here: argument_parsing__3_advanced__gen_prog_template.sh, near the bottom. Modified from: 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 Technique 3 (requires another line which MUST be outside all functions): Modified from: How to detect if a script is being sourced # A. Place this line OUTSIDE all functions: (return 0 2>/dev/null) && script_is_being_executed="false" || script_is_being_executed="true" # B. Place these lines anywhere if [ "$script_is_being_executed" == "true" ]; then echo " This script is being EXECUTED." run="true" else echo " This script is being SOURCED." fi Technique 4 [Limitation: does not handle nested scripts!] (MUST be inside a function): Modified from: How to detect if a script is being sourced and Unix & Linux: How to define a shell script to be sourced not run. 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 This is where I first learned about the ${FUNCNAME[-1]} trick: @mr.spuratic: How to detect if a script is being sourced - he learned it from Dennis Williamson apparently.
参见:
[我的回答]bash相当于Python的if __name__ == '__main__'? [我的回答]Unix和Linux:如何定义一个shell脚本来获取而不是运行
编者注:这个答案的解决方案工作稳健,但只有bash。它可以简化为 (返回2 > / dev / null)。
博士TL;
尝试执行return语句。如果脚本没有来源,则会引发错误。您可以捕获该错误并按照需要进行操作。
把它放在一个文件中,并调用它,比如test.sh:
#!/usr/bin/env sh
# Try to execute a `return` statement,
# but do it in a sub-shell and catch the results.
# If this script isn't sourced, that will raise an error.
$(return >/dev/null 2>&1)
# What exit code did that give?
if [ "$?" -eq "0" ]
then
echo "This script is sourced."
else
echo "This script is not sourced."
fi
直接执行:
shell-prompt> sh test.sh
output: This script is not sourced.
来源:
shell-prompt> source test.sh
output: This script is sourced.
对我来说,这可以在zsh和bash中工作。
解释
如果您试图在函数之外执行return语句,或者如果脚本不是源代码,则return语句将引发错误。在shell提示符中尝试以下操作:
shell-prompt> return
output: ...can only `return` from a function or sourced script
你不需要看到错误消息,所以你可以将输出重定向到dev/null:
shell-prompt> return >/dev/null 2>&1
现在检查逃生码。0表示OK(没有发生错误),1表示发生错误:
shell-prompt> echo $?
output: 1
您还希望在子shell中执行return语句。当return语句运行它时…嗯……的回报。如果在子shell中执行它,它将从子shell返回,而不是从脚本返回。要在子shell中执行,请将其包装在$(…)中:
shell-prompt> $(return >/dev/null 2>$1)
现在,你可以看到子shell的退出代码,它应该是1,因为在子shell内部引发了一个错误:
shell-prompt> echo $?
output: 1
这是从其他一些关于“通用”跨壳支持的答案衍生出来的。不可否认,这与https://stackoverflow.com/a/2942183/3220983非常相似,尽管略有不同。这样做的缺点是,客户端脚本必须尊重如何使用它(即先导出一个变量)。它的优点是简单,而且可以在“任何地方”工作。这里有一个模板供你剪切和粘贴:
# NOTE: This script may be used as a standalone executable, or callable library.
# To source this script, add the following *prior* to including it:
# export ENTRY_POINT="$0"
main()
{
echo "Running in direct executable context!"
}
if [ -z "${ENTRY_POINT}" ]; then main "$@"; fi
注意:我使用export只是为了确保这个机制可以扩展到子进程。