我有一个脚本,我不希望它调用退出,如果它是来源。
我想检查是否$0 == bash,但这有问题,如果脚本是从另一个脚本,或者如果用户从不同的shell,如ksh源。
是否有一种可靠的方法来检测脚本是否被引用?
我有一个脚本,我不希望它调用退出,如果它是来源。
我想检查是否$0 == bash,但这有问题,如果脚本是从另一个脚本,或者如果用户从不同的shell,如ksh源。
是否有一种可靠的方法来检测脚本是否被引用?
当前回答
直截了当:您必须计算变量“$0”是否等于Shell的名称。
是这样的:
#!/bin/bash
echo "First Parameter: $0"
echo
if [[ "$0" == "bash" ]] ; then
echo "The script was sourced."
else
echo "The script WAS NOT sourced."
fi
通过壳:
$ bash check_source.sh
First Parameter: check_source.sh
The script WAS NOT sourced.
通过来源:
$ source check_source.sh
First Parameter: bash
The script was sourced.
很难有一种100%可移植的方法来检测脚本是否来自源代码。
根据我的经验(使用Shellscripting 7年),唯一安全的方法(不依赖具有pid等的环境变量,这是不安全的,因为它是变量),你应该:
扩展你的“如果”的可能性 使用开关/箱子,如果你想。
这两个选项都不能自动缩放,但这是更安全的方式。
例如:
当您通过SSH会话源脚本时,变量“$0”(当使用source时)返回的值是-bash。
#!/bin/bash
echo "First Parameter: $0"
echo
if [[ "$0" == "bash" || "$0" == "-bash" ]] ; then
echo "The script was sourced."
else
echo "The script WAS NOT sourced."
fi
OR
#!/bin/bash
echo "First Parameter: $0"
echo
if [[ "$0" == "bash" ]] ; then
echo "The script was sourced."
elif [[ "$0" == "-bash" ]] ; then
echo "The script was sourced via SSH session."
else
echo "The script WAS NOT sourced."
fi
其他回答
直截了当:您必须计算变量“$0”是否等于Shell的名称。
是这样的:
#!/bin/bash
echo "First Parameter: $0"
echo
if [[ "$0" == "bash" ]] ; then
echo "The script was sourced."
else
echo "The script WAS NOT sourced."
fi
通过壳:
$ bash check_source.sh
First Parameter: check_source.sh
The script WAS NOT sourced.
通过来源:
$ source check_source.sh
First Parameter: bash
The script was sourced.
很难有一种100%可移植的方法来检测脚本是否来自源代码。
根据我的经验(使用Shellscripting 7年),唯一安全的方法(不依赖具有pid等的环境变量,这是不安全的,因为它是变量),你应该:
扩展你的“如果”的可能性 使用开关/箱子,如果你想。
这两个选项都不能自动缩放,但这是更安全的方式。
例如:
当您通过SSH会话源脚本时,变量“$0”(当使用source时)返回的值是-bash。
#!/bin/bash
echo "First Parameter: $0"
echo
if [[ "$0" == "bash" || "$0" == "-bash" ]] ; then
echo "The script was sourced."
else
echo "The script WAS NOT sourced."
fi
OR
#!/bin/bash
echo "First Parameter: $0"
echo
if [[ "$0" == "bash" ]] ; then
echo "The script was sourced."
elif [[ "$0" == "-bash" ]] ; then
echo "The script was sourced via SSH session."
else
echo "The script WAS NOT sourced."
fi
$_是很脆弱的。你必须检查它作为你在脚本中做的第一件事。即使这样,它也不保证包含shell的名称(如果是源的)或脚本的名称(如果是执行的)。
例如,如果用户设置了BASH_ENV,那么在脚本的顶部,$_包含BASH_ENV脚本中执行的最后一个命令的名称。
我发现最好的方法是像这样使用0美元:
name="myscript.sh"
main()
{
echo "Script was executed, running main..."
}
case "$0" in *$name)
main "$@"
;;
esac
不幸的是,这种方式在zsh中并不能开箱使用,因为functionargzero选项的功能超出了它的名称,并且在默认情况下是打开的。
为了解决这个问题,我把unsetopt functionarg0放在我的.zshenv中。
我认为在ksh和bash中没有任何可移植的方法来做到这一点。在bash中,您可以使用调用器输出来检测它,但我认为ksh中不存在等效的输出。
如果您的Bash版本知道BASH_SOURCE数组变量,请尝试如下操作:
# man bash | less -p BASH_SOURCE
#[[ ${BASH_VERSINFO[0]} -le 2 ]] && echo 'No BASH_SOURCE array variable' && exit 1
[[ "${BASH_SOURCE[0]}" != "${0}" ]] && echo "script ${BASH_SOURCE[0]} is being sourced ..."
bash, ksh, zsh的健壮解决方案,包括一个跨shell解决方案,以及一个相当健壮的posix兼容解决方案:
给出的版本号是验证功能的版本号——很可能,这些解决方案也适用于更早的版本——欢迎反馈。 仅使用POSIX特性(例如在dash中,它在Ubuntu中充当/bin/sh),没有可靠的方法来确定脚本是否被引用-请参阅下面的最佳近似方法。
重要的是:
The solutions determine whether the script is being sourced by its caller, which may be a shell itself or another script (which may or may not be sourced itself): Also detecting the latter case adds complexity; if you do not need to detect the case when your script is being sourced by another script, you can use the following, relatively simple POSIX-compliant solution: # Helper function is_sourced() { if [ -n "$ZSH_VERSION" ]; then case $ZSH_EVAL_CONTEXT in *:file:*) return 0;; esac else # Add additional POSIX-compatible shell names here, if needed. case ${0##*/} in dash|-dash|bash|-bash|ksh|-ksh|sh|-sh) return 0;; esac fi return 1 # NOT sourced. } # Sample call. is_sourced && sourced=1 || sourced=0 All solutions below must run in the top-level scope of your script, not inside functions.
下面是一行程序——解释如下;跨shell版本是复杂的,但它应该可以健壮地工作:
Bash(在3.57、4.4.19和5.1.16上验证)
(return 0 2>/dev/null) && sourced=1 || sourced=0
KSH(在93u+上验证)
[[ "$(cd -- "$(dirname -- "$0")" && pwd -P)/$(basename -- "$0")" != "$(cd -- "$(dirname -- "${.sh.file}")" && pwd -P)/$(basename -- "${.sh.file}")" ]] && sourced=1 || sourced=0
ZSH(5.0.5验证)
[[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0
交叉shell (bash, ksh, zsh)
(
[[ -n $ZSH_VERSION && $ZSH_EVAL_CONTEXT =~ :file$ ]] ||
[[ -n $KSH_VERSION && "$(cd -- "$(dirname -- "$0")" && pwd -P)/$(basename -- "$0")" != "$(cd -- "$(dirname -- "${.sh.file}")" && pwd -P)/$(basename -- "${.sh.file}")" ]] ||
[[ -n $BASH_VERSION ]] && (return 0 2>/dev/null)
) && sourced=1 || sourced=0
posix兼容;由于技术原因,不是一行程序(单一管道),并且不完全健壮(见底部):
sourced=0
if [ -n "$ZSH_VERSION" ]; then
case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac
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
(return 0 2>/dev/null) && sourced=1
else # All other shells: examine $0 for known shell binary filenames.
# Detects `sh` and `dash`; add additional shell filenames as needed.
case ${0##*/} in sh|-sh|dash|-dash) sourced=1;; esac
fi
解释
bash
(return 0 2>/dev/null) && sourced=1 || sourced=0
注意:该技术改编自user5754163的答案,因为它比原来的解决方案更健壮,[[$0 != "$BASH_SOURCE"]] && sourced=1 || sourced=0[1]
Bash allows return statements only from functions and, in a script's top-level scope, only if the script is sourced. If return is used in the top-level scope of a non-sourced script, an error message is emitted, and the exit code is set to 1. (return 0 2>/dev/null) executes return in a subshell and suppresses the error message; afterwards the exit code indicates whether the script was sourced (0) or not (1), which is used with the && and || operators to set the sourced variable accordingly. Use of a subshell is necessary, because executing return in the top-level scope of a sourced script would exit the script. Tip of the hat to @Haozhun, who made the command more robust by explicitly using 0 as the return operand; he notes: per bash help of return [N]: "If N is omitted, the return status is that of the last command." As a result, the earlier version [which used just return, without an operand] produces incorrect result if the last command on the user's shell has a non-zero return value.
ksh
[[ "$(cd -- "$(dirname -- "$0")" && pwd -P)/$(basename -- "$0")" != "$(cd -- "$(dirname -- "${.sh.file}")" && pwd -P)/$(basename -- "${.sh.file}")" ]] && sourced=1 || sourced=0
特殊变量${.sh。file}有点类似于$BASH_SOURCE;注意${.sh。File}会在bash、zsh和dash中导致语法错误,所以一定要在多shell脚本中有条件地执行它。
与bash不同,$0和${.sh。file}不能保证是相同的-在不同的时候,一个可能是相对路径或仅仅是文件名,而另一个可能是完整的文件名;因此,$0和${.sh。File}在比较之前必须解析为全路径。如果完整路径不同,则隐含了源。
zsh
[[ $ZSH_EVAL_CONTEXT =~ :file$) ]] && sourced=1 || sourced=0
$ZSH_EVAL_CONTEXT包含关于计算上下文:子字符串文件的信息,用:分隔,只有在脚本被引用时才会出现。
在源脚本的顶级作用域中,$ZSH_EVAL_CONTEXT以:file结尾,这就是这个测试的限制。在函数内部,:shfunc被追加到:file;在命令替换中追加:cmdsubst。
仅使用POSIX特性
如果您愿意做出某些假设,那么您可以根据可能正在执行脚本的shell的二进制文件名,对脚本是否被引用做出合理但并非万无一错的猜测。 值得注意的是,这意味着当您的脚本被另一个脚本引用时,这种方法不会检测到这种情况。
本回答中的“如何处理源调用”一节只详细讨论了POSIX特性无法处理的边缘情况。
检查二进制文件名依赖于$0的标准行为,例如,zsh就没有这种行为。
因此,最安全的方法是将上述健壮的、特定于shell的方法(不依赖于$0)与所有剩余shell的基于$0的回退解决方案结合起来。
简而言之:解决方案如下:
在包含特定于shell的测试的shell中:稳定地工作。 在所有其他shell中:仅当脚本直接来自这样的shell(而不是来自另一个脚本)时才能正常工作。
向Stéphane Desneux和他的灵感答案致敬(将我的跨shell语句表达式转换为sh兼容的if语句,并为其他shell添加一个处理程序)。
sourced=0
if [ -n "$ZSH_VERSION" ]; then
case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac
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
(return 0 2>/dev/null) && sourced=1
else # All other shells: examine $0 for known shell binary filenames.
# Detects `sh` and `dash`; add additional shell filenames as needed.
case ${0##*/} in sh|-sh|dash|-dash) sourced=1;; esac
fi
注意,为了健壮性,每个shell二进制文件名(例如sh)都表示了两次——一次是原样,第二次是,前缀是-。这是为了考虑诸如macOS这样的环境,其中交互式shell作为登录shell启动,其自定义值为$0,该值是前缀为-的(无路径)shell文件名。谢谢,t7e。 (虽然sh和dash可能不太可能用作交互式shell,但您可能需要将其他shell添加到列表中。)
[1] user1902689发现[[$0 != "$BASH_SOURCE"]]在通过将其文件名传递给bash二进制文件执行位于$PATH中的脚本时,会产生假阳性;例如,bash my-script,因为$0只是my-script,而$BASH_SOURCE是完整路径。虽然您通常不会使用这种技术来调用$PATH中的脚本—您只会直接调用它们(my-script)—当与-x结合使用时,它对调试很有帮助。