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

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

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


当前回答

我最终检查[[$_ == "$(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

其他回答

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

我将给出一个特定于bash的答案。Korn shell,对不起。假设脚本名为include2.sh;然后在include2.sh中创建一个名为am_I_sourced的函数。下面是我的include2.sh的演示版本:

am_I_sourced()
{
  if [ "${FUNCNAME[1]}" = source ]; then
    if [ "$1" = -v ]; then
      echo "I am being sourced, this filename is ${BASH_SOURCE[0]} and my caller script/shell name was $0"
    fi
    return 0
  else
    if [ "$1" = -v ]; then
      echo "I am not being sourced, my script/shell name was $0"
    fi
    return 1
  fi
}

if am_I_sourced -v; then
  echo "Do something with sourced script"
else
  echo "Do something with executed script"
fi

现在尝试以多种方式执行它:

~/toys/bash $ chmod a+x include2.sh

~/toys/bash $ ./include2.sh 
I am not being sourced, my script/shell name was ./include2.sh
Do something with executed script

~/toys/bash $ bash ./include2.sh 
I am not being sourced, my script/shell name was ./include2.sh
Do something with executed script

~/toys/bash $ . include2.sh
I am being sourced, this filename is include2.sh and my caller script/shell name was bash
Do something with sourced script

所以这是毫无例外的工作,它没有使用脆弱的$_东西。这个技巧使用了BASH的自省功能,即内置变量FUNCNAME和BASH_SOURCE;请参阅bash手册页中的文档。

只有两个警告:

1)对am_I_called的调用必须发生在源脚本中,而不是在任何函数中,以免${FUNCNAME[1]}返回其他东西。是的…您本可以检查${FUNCNAME[2]},但这样做只会使您的工作更加困难。

2)函数am_I_called必须驻留在源脚本中,如果你想知道被包含的文件的名称。

检测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脚本来获取而不是运行

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

这似乎在Bash和Korn之间是可移植的:

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

与此类似的行或赋值语句如pathname="$_"(带有稍后的测试和操作)必须位于脚本的第一行或shebang之后的行(如果使用了shebang,则应该用于ksh,以便它在大多数情况下工作)。