我有一个脚本,我不希望它调用退出,如果它是来源。

我想检查是否$0 == bash,但这有问题,如果脚本是从另一个脚本,或者如果用户从不同的shell,如ksh源。

是否有一种可靠的方法来检测脚本是否被引用?


当前回答

我想对丹尼斯非常有用的回答提出一个小小的更正,让它更容易携带,我希望:

[ "$_" != "$0" ] && echo "Script is being sourced" || echo "Script is a subshell"

因为[[不被Debian POSIX兼容外壳识别(有些保留的IMHO), dash。同样,在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

BASH_SOURCE[]的答案(bash-3.0及更高版本)似乎最简单,尽管BASH_SOURCE[]并没有在函数体之外工作(它目前恰好可以工作,这与手册页不一致)。

Wirawan Purwanto建议的最健壮的方法是在函数中检查FUNCNAME[1]:

function mycheck() { declare -p FUNCNAME; }
mycheck

然后:

$ bash sourcetest.sh
declare -a FUNCNAME='([0]="mycheck" [1]="main")'
$ . sourcetest.sh
declare -a FUNCNAME='([0]="mycheck" [1]="source")'

This is the equivalent to checking the output of caller, the values main and source distinguish the caller's context. Using FUNCNAME[] saves you capturing and parsing caller output. You need to know or calculate your local call depth to be correct though. Cases like a script being sourced from within another function or script will cause the array (stack) to be deeper. (FUNCNAME is a special bash array variable, it should have contiguous indexes corresponding to the call stack, as long as it is never unset.)

function issourced() {
    [[ ${FUNCNAME[@]: -1} == "source" ]]
}

(在bash-4.2及以后版本中,可以使用更简单的形式${FUNCNAME[-1]}来代替数组中的最后一项。感谢Dennis Williamson下面的评论,改进和简化了。)

然而,你的问题如所述是“我有一个脚本,我不希望它调用'退出',如果它是来源”。这种情况下常见的bash习惯用法是:

return 2>/dev/null || exit

如果脚本是源脚本,则return将终止源脚本并返回给调用者。

如果脚本正在执行,那么return将返回一个错误(重定向),exit将正常终止脚本。如果需要,return和exit都可以使用退出码。

遗憾的是,这在ksh中不起作用(至少在我这里的AT&T派生版本中不起作用),如果在函数或点源脚本外部调用return,它将return视为等同于exit。

更新:在当前版本的ksh中,您可以检查特殊变量.sh。设置为函数调用深度的级别。对于一个被调用的脚本,它最初将被取消设置,对于一个点源脚本,它将被设置为1。

function issourced {
    [[ ${.sh.level} -eq 2 ]]
}

issourced && echo this script is sourced

这并不像bash版本那样健壮,您必须在正在测试的文件的顶层或已知函数深度中调用issourced()。

(你可能也对github上的这段代码感兴趣,它使用了ksh纪律函数和一些调试陷阱技巧来模拟bash FUNCNAME数组。)

这里的标准答案是:http://mywiki.wooledge.org/BashFAQ/109还提供了$-作为shell状态的另一个指示器(尽管并不完善)。


注:

可以创建名为“main”和“source”的bash函数(覆盖内置),这些名称可能出现在FUNCNAME[]中,但只要只测试该数组中的最后一项,就不会有歧义。 我对pdksh没有一个好的答案。我能找到的最接近的方法只适用于pdksh,其中脚本的每个源都打开一个新的文件描述符(原始脚本从10开始)。几乎肯定不是你想要依靠的东西……

使用shebang行并检查它是否正在执行。

你的脚本应该有一行shebang #!/path/to/shell表示它应该在哪个shell中运行。否则,您还会遇到其他跨shell兼容性问题。

因此,您只需要通过尝试在源时不起作用的命令来检查它是否正在执行。

如。对于Bash脚本:

#!/usr/bin/env bash

if (return 0 2>/dev/null); then
    echo "Script was sourced."
fi

这个方法也适用于zsh和sh只是改变shebang行。

@mklement0答案的一个小补充。这是我在脚本中使用的自定义函数,用于确定它是否被引用:

replace_shell(){
   if [ -n "$ZSH_EVAL_CONTEXT" ]; then 
      case $ZSH_EVAL_CONTEXT in *:file*) echo "Zsh is sourced";; esac
   else
      case ${0##*/} in sh|dash|bash) echo "Bash is sourced";; esac
   fi
}

在函数中,zsh的“$ZSH_EVAL_CONTEXT”的输出是toplevel:file:shfunc,而不仅仅是toplevel:file;因此,*:file*应该解决这个问题。

直截了当:您必须计算变量“$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