我有一个脚本,我不希望它调用退出,如果它是来源。
我想检查是否$0 == bash,但这有问题,如果脚本是从另一个脚本,或者如果用户从不同的shell,如ksh源。
是否有一种可靠的方法来检测脚本是否被引用?
我有一个脚本,我不希望它调用退出,如果它是来源。
我想检查是否$0 == bash,但这有问题,如果脚本是从另一个脚本,或者如果用户从不同的shell,如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 ..."
其他回答
我最终检查[[$_ == "$(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
这并不完全是OP想要的,但我经常发现自己需要源代码脚本只是为了加载它的函数(即作为一个库)。例如,用于基准测试或测试目的。
下面是一个适用于所有shell(包括POSIX)的设计:
将所有顶级操作包装在run_main()函数中。 让您的源脚本检查不执行任何操作的初始-no-run参数;没有——no-run,它可以调用run_main。 使用以下方法获取脚本的源代码:
set -- --no-run "$@"
. script.sh
shift
问题是。或者来源是不可能将参数可移植地传递给脚本。POSIX shell忽略参数。并传递调用者的“$@”。
我遵循mklement0紧凑表达式。
这很整洁,但我注意到,当调用ksh时,它可能会失败:
/bin/ksh -c ./myscript.sh
(它认为它是源的,而不是因为它执行了一个子shell) 但是表达式可以检测到这一点:
/bin/ksh ./myscript.sh
此外,即使表达式是紧凑的,语法也不兼容所有shell。
因此,我以以下代码结束,它适用于bash,zsh,dash和ksh
SOURCED=0
if [ -n "$ZSH_EVAL_CONTEXT" ]; then
[[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && SOURCED=1
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
[[ $0 != "$BASH_SOURCE" ]] && SOURCED=1
elif grep -q dash /proc/$$/cmdline; then
case $0 in *dash*) SOURCED=1 ;; esac
fi
请随意添加异国情调的贝壳支持:)
使用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行。
编者注:这个答案的解决方案工作稳健,但只有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