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

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

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


当前回答

@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*应该解决这个问题。

其他回答

这在后面的脚本中起作用,不依赖于_变量:

## Check to make sure it is not sourced:
Prog=myscript.sh
if [ $(basename $0) = $Prog ]; then
   exit 1  # not sourced
fi

or

[ $(basename $0) = $Prog ] && exit

我最终检查[[$_ == "$(type -p "$0")"]]

if [[ $_ == "$(type -p "$0")" ]]; then
    echo I am invoked from a sub shell
else
    echo I am invoked from a source command
fi

当使用curl时…| bash -s——ARGS运行远程脚本时,$0将只是bash而不是正常的/bin/bash运行实际的脚本文件,所以我使用-p“$0”来显示bash的完整路径。

测试:

curl -sSL https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath | bash -s -- /a/b/c/d/e /a/b/CC/DD/EE

source <(curl -sSL https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath)
relpath /a/b/c/d/e /a/b/CC/DD/EE

wget https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath
chmod +x relpath
./relpath /a/b/c/d/e /a/b/CC/DD/EE

@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*应该解决这个问题。

这是从其他一些关于“通用”跨壳支持的答案衍生出来的。不可否认,这与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只是为了确保这个机制可以扩展到子进程。

解决这个问题的方法不是编写需要知道这些事情才能正确运行的代码。做到这一点的方法是将代码放入函数中,而不是放入需要源代码的脚本主线中。

函数内部的代码可以只返回0或1。这只终止了函数,因此控制返回到调用该函数的任何对象。

无论从源脚本的主线调用函数,还是从顶级脚本的主线调用函数,或者从另一个函数调用函数,都是如此。

使用sourcing来引入“库”脚本,这些脚本只定义函数和变量,但实际上不执行任何其他顶级命令:

. path/to/lib.sh # defines libfunction
libfunction arg

否则:

path/to/script.sh arg # call script as a child process

而不是:

. path/to/script.sh arg  # shell programming anti-pattern