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

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

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


当前回答

我认为在ksh和bash中没有任何可移植的方法来做到这一点。在bash中,您可以使用调用器输出来检测它,但我认为ksh中不存在等效的输出。

其他回答

使用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行。

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

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

$_是很脆弱的。你必须检查它作为你在脚本中做的第一件事。即使这样,它也不保证包含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中。

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