我有一个脚本,如果它被采购,我不希望它调用exit
。
我想过检查是否$0 == bash
但如果脚本来自另一个脚本,或者如果用户从ksh
这样的不同shell中获取它,则会出现问题。
是否有可靠的方法来检测脚本是否来源?
这似乎可以在Bash和Korn之间移植:
[[ $_ != $0 ]] && echo "Script is being sourced" || echo "Script is a subshell"
与此类似的行或类似pathname="$_"
(后面的测试和操作)的赋值必须在脚本的第一行或shebang之后的行上(如果使用的话,应该用于ksh以使其工作)在大多数情况下)。
我想建议对Dennis' very helpful answer做一个小修正,使其稍微便于携带,我希望:
[ "$_" != "$0" ] && echo "Script is being sourced" || echo "Script is a subshell"
因为[[
不被(有点肛门保持恕我直言)Debian POSIX兼容外壳,dash
认可。此外,还可以在所述shell中使用引号来防止包含空格的文件名。
$_
非常脆弱。您必须在脚本中首先检查它。即便如此,也不能保证包含shell的名称(如果是源代码)或脚本的名称(如果已执行)。
例如,如果用户已设置BASH_ENV
,则在脚本顶部,$_
包含BASH_ENV
脚本中执行的最后一个命令的名称。
我找到的最好方法是使用$0
这样:
name="myscript.sh"
main()
{
echo "Script was executed, running main..."
}
case "$0" in *$name)
main "$@"
;;
esac
不幸的是,由于functionargzero
选项的功能超出了它的名称,并且默认情况下处于启用状态,因此这种方式在zsh中无法正常使用。
为了解决这个问题,我把unsetopt functionargzero
放在我的.zshenv
中。
我跟着mklement0 compact expression。
这很整洁,但是我注意到在调用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
随意添加异国情调的贝壳支持:)
我不认为在ksh和bash中都有任何可移植的方法。在bash中你可以使用caller
输出检测它,但我不认为在ksh中存在等价物。
我需要一个可以在[mac,linux]上使用bash.version> = 3的单行程,这些答案都不符合要求。
[[ ${BASH_SOURCE[0]} = $0 ]] && main "$@"
直截了当:您必须评估变量“$ 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
通过SHELL:
$ bash check_source.sh
First Parameter: check_source.sh
The script WAS NOT sourced.
通过SOURCE:
$ source check_source.sh
First Parameter: bash
The script was sourced.
使用100%可移植的方式来检测脚本是否来源是非常困难的。
关于我的经验(使用Shellscripting 7年),唯一安全的方法(不依赖于具有PID等的环境变量,由于它是VARIABLE的事实而不安全),您应该:
这两个选项都无法自动缩放,但它是更安全的方式。
例如:
当您通过SSH会话获取脚本时,变量“$ 0”(使用源代码时)返回的值为-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
要么
#!/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
在运行实际脚本文件时,所以我使用type -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
如果您的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 ..."
针对bash
,ksh
,zsh
的强大解决方案,包括跨shell解决方案,以及相当强大的POSIX兼容解决方案:
dash
中,在Ubuntu上充当/bin/sh
),没有可靠的方法来确定脚本是否来源 - 请参阅下面的最佳近似。单线跟随 - 下面解释;跨shell版本很复杂,但它应该可靠地运行:
(return 0 2>/dev/null) && sourced=1 || sourced=0
[[ $(cd "$(dirname -- "$0")" &&
printf '%s' "${PWD%/}/")$(basename -- "$0") != "${.sh.file}" ]] &&
sourced=1 || sourced=0
[[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0
([[ -n $ZSH_EVAL_CONTEXT && $ZSH_EVAL_CONTEXT =~ :file$ ]] ||
[[ -n $KSH_VERSION && $(cd "$(dirname -- "$0")" &&
printf '%s' "${PWD%/}/")$(basename -- "$0") != "${.sh.file}" ]] ||
[[ -n $BASH_VERSION ]] && (return 0 2>/dev/null)) && sourced=1 || sourced=0
sourced=0
if [ -n "$ZSH_EVAL_CONTEXT" ]; then
case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac
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
(return 0 2>/dev/null) && sourced=1
else # All other shells: examine $0 for known shell binary filenames
# Detects `sh` and `dash`; add additional shell filenames as needed.
case ${0##*/} in sh|dash) sourced=1;; esac
fi
说明:
(return 0 2>/dev/null) && sourced=1 || sourced=0
注意:该技术改编自user5754163's answer,因为它证明比原始解决方案更强大,[[ $0 != "$BASH_SOURCE" ]] && sourced=1 || sourced=0
[1]
return
语句,并且在脚本的顶级范围内,仅在脚本来源时才允许。
如果在非源脚本的顶级范围中使用return
,则会发出错误消息,并且退出代码将设置为1
。(return 0 2>/dev/null)
在子shell中执行return
并抑制错误消息;之后,退出代码表明脚本是否来源(0
)或不是(1
),它与&&
和||
运算符一起用于相应地设置sourced
变量。
使用子shell是必要的,因为在源脚本的顶级范围内执行return
会退出脚本。
@Haozhun的帽子提示,通过明确使用0
作为return
操作数使命令更强大;他指出:根据return [N]
的bash帮助:“如果省略N,则返回状态是最后一个命令的状态。”因此,如果用户shell上的最后一个命令具有非零返回值,则早期版本[仅使用return
,没有操作数]会产生不正确的结果。[[ \
$(cd "$(dirname -- "$0")" && printf '%s' "${PWD%/}/")$(basename -- "$0") != \
"${.sh.file}" \
]] &&
sourced=1 || sourced=0
特殊变量${.sh.file}
有点类似于$BASH_SOURCE
;请注意,${.sh.file}
会在bash,zsh和dash中导致语法错误,因此请务必在多shell脚本中有条件地执行它。
与bash不同,$0
和${.sh.file}
在非源情况下不保证完全相同,因为$0
可能是相对路径,而${.sh.file}
总是完整路径,因此在比较之前必须将$0
解析为完整路径。
[[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0
$ZSH_EVAL_CONTEXT
包含有关评估上下文的信息 - 在函数外部调用它。在源脚本[的顶级范围]内,$ZSH_EVAL_CONTEXT
以:file
结尾。
警告:在命令替换中,zsh附加:cmdsubst
,因此在那里测试$ZSH_EVAL_CONTEXT
的:file:cmdsubst$
。
如果您愿意做出某些假设,那么基于知道可能正在执行脚本的shell的二进制文件名,您可以做出合理的,但不是万无一失的猜测,即您的脚本是否来源。 值得注意的是,这意味着如果您的脚本由另一个脚本提供,此方法将失败。
我在this answer中的“如何处理源调用”一节讨论了仅使用POSIX功能无法详细处理的边缘情况。
这取决于$0
的标准行为,例如zsh
没有表现出来。
因此,最安全的方法是将上面的强大的shell特定方法与所有剩余shell的回退解决方案相结合。
向Stéphane Desneux和his answer提供灵感的帽子的提示(将我的跨shell语句表达式转换为sh
兼容的if
语句并为其他shell添加处理程序)。
sourced=0
if [ -n "$ZSH_EVAL_CONTEXT" ]; then
case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac
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
(return 0 2>/dev/null) && sourced=1
else # All other shells: examine $0 for known shell binary filenames
# Detects `sh` and `dash`; add additional shell filenames as needed.
case ${0##*/} in sh|dash) sourced=1;; esac
fi
[1] user1902689发现[[ $0 != "$BASH_SOURCE" ]]
通过将其仅仅的文件名传递给$PATH
二进制文件来执行位于bash
中的脚本时会产生误报;例如,bash my-script
,因为$0
只是my-script
,而$BASH_SOURCE
是完整的道路。虽然你通常不会使用这种技术来调用$PATH
中的脚本 - 你只需要直接调用它们(my-script
) - 当它与-x
结合进行调试时它会很有用。
阅读@ DennisWilliamson的回答后,有一些问题,见下文:
由于这个问题代表ksh和bash,这个答案中有另一部分关于ksh ...见下文。
[ "$0" = "$BASH_SOURCE" ]
让我们尝试(因为bash可以在飞行中;-):
source <(echo $'#!/bin/bash
[ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
echo "process $$ is $v ($0, $BASH_SOURCE)" ')
process 29301 is sourced (bash, /dev/fd/63)
bash <(echo $'#!/bin/bash
[ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
echo "process $$ is $v ($0, $BASH_SOURCE)" ')
process 16229 is own (/dev/fd/63, /dev/fd/63)
为了便于阅读,我使用source
代替.
(因为.
是source
的别名):
. <(echo $'#!/bin/bash
[ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
echo "process $$ is $v ($0, $BASH_SOURCE)" ')
process 29301 is sourced (bash, /dev/fd/63)
请注意,进程停留源时,进程号不会更改:
echo $$
29301
$_ == $0
comparison为了确保很多情况,我开始写一个真正的脚本:
#!/bin/bash
# As $_ could be used only once, uncomment one of two following lines
#printf '_="%s", 0="%s" and BASH_SOURCE="%s"\n' "$_" "$0" "$BASH_SOURCE"
[[ "$_" != "$0" ]] && DW_PURPOSE=sourced || DW_PURPOSE=subshell
[ "$0" = "$BASH_SOURCE" ] && BASH_KIND_ENV=own || BASH_KIND_ENV=sourced;
echo "proc: $$[ppid:$PPID] is $BASH_KIND_ENV (DW purpose: $DW_PURPOSE)"
将其复制到名为testscript
的文件中:
cat >testscript
chmod +x testscript
现在我们可以测试:
./testscript
proc: 25758[ppid:24890] is own (DW purpose: subshell)
没关系。
. ./testscript
proc: 24890[ppid:24885] is sourced (DW purpose: sourced)
source ./testscript
proc: 24890[ppid:24885] is sourced (DW purpose: sourced)
没关系。
但是,在添加-x
标志之前测试脚本:
bash ./testscript
proc: 25776[ppid:24890] is own (DW purpose: sourced)
或者使用预定义的变量:
env PATH=/tmp/bintemp:$PATH ./testscript
proc: 25948[ppid:24890] is own (DW purpose: sourced)
env SOMETHING=PREDEFINED ./testscript
proc: 25972[ppid:24890] is own (DW purpose: sourced)
这将不再有效。
将评论从第5行移到第6行会给出更可读的答案:
./testscript
_="./testscript", 0="./testscript" and BASH_SOURCE="./testscript"
proc: 26256[ppid:24890] is own
. testscript
_="_filedir", 0="bash" and BASH_SOURCE="testscript"
proc: 24890[ppid:24885] is sourced
source testscript
_="_filedir", 0="bash" and BASH_SOURCE="testscript"
proc: 24890[ppid:24885] is sourced
bash testscript
_="/bin/bash", 0="testscript" and BASH_SOURCE="testscript"
proc: 26317[ppid:24890] is own
env FILE=/dev/null ./testscript
_="/usr/bin/env", 0="./testscript" and BASH_SOURCE="./testscript"
proc: 26336[ppid:24890] is own
由于我没有经常使用ksh,在手册上阅读了一些内容之后,我有尝试:
#!/bin/ksh
set >/tmp/ksh-$$.log
复制到testfile.ksh
:
cat >testfile.ksh
chmod +x testfile.ksh
比跑两次:
./testfile.ksh
. ./testfile.ksh
ls -l /tmp/ksh-*.log
-rw-r--r-- 1 user user 2183 avr 11 13:48 /tmp/ksh-9725.log
-rw-r--r-- 1 user user 2140 avr 11 13:48 /tmp/ksh-9781.log
echo $$
9725
并看到:
diff /tmp/ksh-{9725,9781}.log | grep ^\> # OWN SUBSHELL:
> HISTCMD=0
> PPID=9725
> RANDOM=1626
> SECONDS=0.001
> lineno=0
> SHLVL=3
diff /tmp/ksh-{9725,9781}.log | grep ^\< # SOURCED:
< COLUMNS=152
< HISTCMD=117
< LINES=47
< PPID=9163
< PS1='$ '
< RANDOM=29667
< SECONDS=23.652
< level=1
< lineno=1
< SHLVL=2
在源代码运行中继承了一些变量,但没有真正相关的......
你甚至可以检查$SECONDS
是否接近0.000
,但这确保只有manualy来源的案件......
你甚至可以尝试检查父母是什么:
把它放到你的testfile.ksh
:
ps $PPID
比:
./testfile.ksh
PID TTY STAT TIME COMMAND
32320 pts/4 Ss 0:00 -ksh
. ./testfile.ksh
PID TTY STAT TIME COMMAND
32319 ? S 0:00 sshd: user@pts/4
或ps ho cmd $PPID
,但这项工作仅适用于一个级别的子会话......
对不起,我在ksh下找不到可行的方法。
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")'
这相当于检查caller
的输出,值main
和source
区分调用者的上下文。使用FUNCNAME[]
可以节省捕获和解析caller
输出。您需要知道或计算您的本地呼叫深度才是正确的。像从另一个函数或脚本中获取脚本的情况将导致数组(堆栈)更深。 (FUNCNAME
是一个特殊的bash数组变量,它应该具有与调用堆栈相对应的连续索引,只要它永远不会是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
视为等同于exit
。
更新:在当前版本的ksh
中可以做的是检查设置为函数调用深度的特殊变量.sh.level
。对于调用的脚本,最初将取消设置,对于点源脚本,它将设置为1。
function issourced {
[[ ${.sh.level} -eq 2 ]]
}
issourced && echo this script is sourced
这不像bash版本那么强大,你必须在顶级或已知函数深度的文件中调用issourced()
。
(您可能也对github上的this code感兴趣,它使用ksh
规则函数和一些调试陷阱技巧来模拟bash FUNCNAME
数组。)
这里的规范答案:http://mywiki.wooledge.org/BashFAQ/109还提供$-
作为贝壳状态的另一个指标(虽然不完美)。
笔记:
FUNCNAME[]
中,但只要测试该数组中的最后一项,就没有歧义。pdksh
没有一个好的答案。我能找到的最接近的东西仅适用于pdksh
,其中每个脚本的源代码都会打开一个新的文件描述符(从原始脚本的10开始)。几乎肯定不是你想要依赖的东西......编者注:这个答案的解决方案运行稳健,但仅限于bash
。它可以简化为
(return 2>/dev/null)
。
TL; DR
尝试执行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
语句将引发错误。从shell提示符处尝试:
shell-prompt> return
output: ...can only `return` from a function or sourced script
您不需要查看该错误消息,因此您可以将输出重定向到dev / null:
shell-prompt> return >/dev/null 2>&1
现在检查退出代码。 0表示正常(未发生错误),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
FWIW,在阅读了所有其他答案之后,我想出了以下解决方案:
更新:实际上somebody spottet an error in another answer也影响我的。我认为,这里的更新也是一种改进(如果你很好奇,请参阅编辑)。
这适用于所有脚本,它们以#!/bin/bash
开头,但也可能由不同的shell提供。
#!/bin/bash
# Function definitions (API) and shell variables (constants) go here
main()
{
# The script's execution part goes here
}
BASH_SOURCE=".$0" # cannot be changed in bash
test ".$0" != ".$BASH_SOURCE" || main "$@"
您可以使用以下(在我看来不太可读)的代码来代替最后两行代码,而不是在其他shell中设置BASH_SOURCE
并允许set -e
在main
中工作:
if ( BASH_SOURCE=".$0" && exec test ".$0" != ".$BASH_SOURCE" ); then :; else main "$@"; fi
此脚本配方具有以下属性:
bash
以正常方式执行,则调用main
。请注意,这不包括像bash -x script
这样的调用(其中script
不包含路径),请参阅下文。bash
提供,则只调用main
,如果调用脚本恰好具有相同的名称。 (例如,如果它自己或通过bash -c 'someotherscript "$@"' main-script args..
必须是main-script
来源,test
认为$BASH_SOURCE
)。eval
以外的shell获取/执行/读取/ bash
ed,则不调用main
(BASH_SOURCE
总是与$0
不同)。main
从stdin读取脚本,则不调用bash
,除非你将$0
设置为空字符串,如下所示:( exec -a '' /bin/bash ) <script
bash
使用eval
(eval "`cat script`"
所有引用都很重要!)从其他脚本中进行评估,则会调用main
。如果eval
直接从命令行运行,这与之前的情况类似,其中从stdin读取脚本。 (BASH_SOURCE
是空白的,而$0
通常是/bin/bash
,如果没有强迫完全不同的东西。)main
,它确实返回true
($?=0
)。unset
也不改变BASH_SOURCE
):
BASH_SOURCE
is a bash reserved array。但是允许BASH_SOURCE=".$0"
改变它会打开一个非常危险的蠕虫,所以我的期望是,这一定无效(除了,或许,在某些未来版本的bash
中会出现一些丑陋的警告)。
BASH_SOURCE
没有文档在函数之外工作。然而,相反(它只在函数中起作用)没有记录。观察是,它运行(用bash
v4.3和v4.4进行测试,不幸的是我没有bash
v3.x了)并且如果$BASH_SOURCE
停止工作,那么太多的脚本会破坏。因此,我的期望是,BASH_SOURCE
也保留了未来版本的bash
。
相比之下(很好的发现,BTW!)考虑( return 0 )
,如果没有来源,它会给0
和1
。 This comes a bit unexpected not only for me ,和(根据那里的读数)POSIX说,来自子壳的return
是未定义的行为(这里的return
显然来自子壳)。也许这个功能最终得到了足够的广泛使用,以至于它不能再被改变,但AFAICS有一个更高的机会,在这种情况下,某些未来的bash
版本会意外地改变返回行为。bash -x script 1 2 3
没有运行main
。 (比较script 1 2 3
,其中script
没有路径)。以下可用作解决方法:
bash -x "`which script`" 1 2 3
bash -xc '. script' "`which script`" 1 2 3
那bash script 1 2 3
没有运行main
可以被认为是一个功能。( exec -a none script )
调用main
(bash
没有将它的$0
传递给脚本,为此你需要使用-c
,如最后一点所示)。因此,除了一些角落情况之外,当脚本以通常的方式执行时,仅调用main
。通常这就是你想要的,特别是因为它缺乏复杂难以理解的代码。
请注意,它与Python代码非常相似:
if __name__ == '__main__': main()
这也阻止调用
main
,除了一些极端情况,因为你可以导入/加载脚本并强制执行__name__='__main__'
如果您有可以由多个shell获取的内容,则必须兼容。但是(阅读其他答案),因为没有(易于实现)可移植的方式来检测source
ing,你应该改变规则。
通过强制执行脚本必须由/bin/bash
执行,您完全是这样做的。
这解决了所有情况,但在这种情况下脚本无法直接运行:
/bin/bash
没有安装或功能不正常(即在启动环境中使用E.)curl https://example.com/script | $SHELL
这样的shell但是,我无法考虑您需要它的任何真正原因,也无法并行获取完全相同的脚本!通常你可以包装它来手动执行main
。像那样:
$SHELL -c '. script && main'
{ curl https://example.com/script && echo && echo main; } | $SHELL
$SHELL -c 'eval "`curl https://example.com/script`" && main'
echo 'eval "`curl https://example.com/script`" && main' | $SHELL
我将给出一个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
函数必须位于源脚本中。
这将在稍后的脚本中工作,并且不依赖于_变量:
## Check to make sure it is not sourced:
Prog=myscript.sh
if [ $(basename $0) = $Prog ]; then
exit 1 # not sourced
fi
要么
[ $(basename $0) = $Prog ] && exit