我有一个脚本,如果它被采购,我不希望它调用exit
。
我想过检查是否$0 == bash
但如果脚本来自其他脚本,或者用户从其他shell(如ksh
)获取脚本,则会出现问题。
是否有可靠的方法来检测脚本是否来源?
答案 0 :(得分:139)
如果您的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 ..."
答案 1 :(得分:86)
bash
,ksh
,zsh
的强大解决方案,包括跨shell ,以及相当强大的POSIX兼容解决方案:
给出的版本号是验证功能的版本号 - 可能这些解决方案也适用于早期版本 - 反馈欢迎。
仅使用 POSIX功能(例如dash
,在Ubuntu上充当/bin/sh
),没有健壮< / em> way 确定脚本是否来源 - 请参阅下面的最佳近似。
单行跟随 - 解释如下;跨shell版本很复杂,但它应该可靠地运行:
bash (已在3.57和4.4.19验证)
(return 0 2>/dev/null) && sourced=1 || sourced=0
ksh (已在93u +验证)
[[ $(cd "$(dirname -- "$0")" &&
printf '%s' "${PWD%/}/")$(basename -- "$0") != "${.sh.file}" ]] &&
sourced=1 || sourced=0
zsh (在5.0.5上验证) - 务必在函数外部调用
[[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0
cross-shell(bash,ksh,zsh)
([[ -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
<强>符合POSIX标准强>;由于技术原因而不是单行(单一管道),不完全健壮(见下):
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]
Bash只允许来自函数的return
语句,并且在脚本的顶级作用域中,仅当脚本 sourced 时才允许使用return
语句。
1
,则会发出错误消息,并且退出代码设置为(return 0 2>/dev/null)
。 return
在子shell 中执行0
并禁止显示错误消息;然后退出代码指示脚本是否来源(1
)或不是&&
),它与||
和sourced
运算符一起用于设置{{1相应的变量。
return
会退出脚本。 0
作为return
操作数,使命令更加健壮;他指出:return [N]
的每个bash帮助:&#34;如果省略N,则返回状态是最后一个命令的状态。&#34;因此,早期版本[仅使用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
以<{1}}结束。
警告:在命令替换中,zsh附加:file
,因此在那里:cmdsubst
测试$ZSH_EVAL_CONTEXT
。
如果您愿意做出某些假设,那么可以做出合理的,但不是万无一失的猜测 ,以确定您的脚本是否来源,基于 知道可能正在执行脚本的shell的二进制文件名 。
值得注意的是,这意味着如果您的脚本来自另一个脚本,则此方法将失败。
部分&#34;如何处理来源的调用&#34;我在this answer中讨论了仅使用POSIX功能无法处理的边缘情况。
此依赖于:file:cmdsubst$
的标准行为,$0
,例如不展示。
因此,最安全的方法是将上面强大的,特定于shell的方法与后备解决方案 结合起来用于所有剩余的shell。
向Stéphane Desneux和his answer提供灵感的提示(将我的跨shell语句表达式转换为zsh
兼容的sh
语句并添加其他shell的处理程序。)
if
[1] user1902689发现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
通过传递[[ $0 != "$BASH_SOURCE" ]] 中的脚本时会产生误报> {i} filename 到$PATH
二进制文件;例如,bash
,因为bash my-script
只是$0
,而my-script
是完整路径。虽然您通常不会使用此技术来调用$BASH_SOURCE
中的脚本 - 您只需直接调用 ($PATH
) - 它对于调试与my-script
结合使用时有用。
答案 2 :(得分:66)
阅读@ 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
比较为了确保很多情况,我开始写一个 true 脚本:
#!/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
,但这只能确保手动案件......
您甚至可以尝试检查父母的:
将其放入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下,我无法找到可行的方法。
答案 3 :(得分:54)
这似乎可以在Bash和Korn之间移植:
[[ $_ != $0 ]] && echo "Script is being sourced" || echo "Script is a subshell"
与此类似的行或类似pathname="$_"
的赋值(后面的测试和操作)必须位于脚本的第一行或shebang之后的行(如果使用的话,应该是ksh,以便它在大多数情况下工作。)
答案 4 :(得分:30)
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]}
代替数组中的最后一项。感谢丹尼斯威廉姆森在下面的评论,改进和简化。)
但是,你所说的问题是“我有一个脚本,我不希望它被称为'退出',如果它被采购”。这种情况的常见bash
成语是:
return 2>/dev/null || exit
如果脚本来源,那么return
将终止源脚本并返回给调用者。
如果正在执行脚本,则return
将返回错误(重定向),exit
将正常终止脚本。如果需要,return
和exit
都可以使用退出代码。
可悲的是,这在ksh
中不起作用(至少在我在这里的AT&amp; T派生版本中没有),如果在外面调用它会将return
视为等同于exit
函数或点源脚本。
已更新:您在<{1}}的当代版本中可以做的是检查设置为函数调用的特殊变量ksh
深度。对于调用的脚本,最初将取消设置,对于点源脚本,它将设置为1.
.sh.level
这不如bash版本强大,您必须在顶级或已知函数深度的文件中调用function issourced {
[[ ${.sh.level} -eq 2 ]]
}
issourced && echo this script is sourced
。
(您可能也对github上的this code感兴趣,它使用issourced()
规则函数和一些调试陷阱技巧来模拟bash ksh
数组。)
这里的规范答案:http://mywiki.wooledge.org/BashFAQ/109还提供FUNCNAME
作为shell状态的另一个指标(尽管不完美)。
注意:
$-
中,但只要在那里测试该数组中的最后一项没有歧义。FUNCNAME[]
没有一个好的答案。我能找到的最接近的东西仅适用于pdksh
,其中每个脚本的源代码都会打开一个新的文件描述符(从原始脚本的10开始)。几乎肯定不是你想要依赖的东西...... 答案 5 :(得分:18)
编者注:这个答案的解决方案运行稳健,但仅限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
答案 6 :(得分:7)
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
以外的来源/执行/阅读/ bash
,main
未被调用(BASH_SOURCE
总是与{{$0
不同1}})。
main
从stdin读取脚本,则不会调用 bash
,除非您将$0
设置为空字符串,如下所示:( exec -a '' /bin/bash ) <script
如果bash
使用eval
(eval "`cat script`"
所有引号都很重要!)从其他脚本中进行评估,则会调用{{1} }}。如果直接从命令行运行main
,则类似于先前的情况,其中从stdin读取脚本。 (eval
为空,而BASH_SOURCE
通常为$0
,如果没有强制完全不同的话。{/ p>
如果未调用/bin/bash
,则会返回main
(true
)。
这不依赖于意外行为(之前我写的是未记录的行为,但我发现没有文档说明你不能$?=0
也不能改变unset
:
BASH_SOURCE
is a bash reserved array。但允许BASH_SOURCE
更改它会打开一个非常危险的蠕虫病毒,所以我的期望是,这一定不会产生任何影响(可能会有一些丑陋的警告出现在BASH_SOURCE=".$0"
的某个未来版本中)。bash
在函数外部工作的文档。然而,相反(它只在函数中起作用)没有记录。观察是,它工作(用BASH_SOURCE
v4.3和v4.4测试,不幸的是我不再有bash
v3.x)并且如果{{1停止工作。因此,我的期望是bash
保留$BASH_SOURCE
的未来版本。BASH_SOURCE
,如果来源则提供bash
,如果没有来源则提供( return 0 )
。 This comes a bit unexpected not only for me ,并且(根据那里的读数)POSIX说,来自subshell的0
是未定义的行为(这里的1
显然来自子shell)。也许这个功能最终得到了足够广泛的使用,以至于它不能再被改变,但是AFAICS在将来return
版本意外改变返回行为的可能性要大得多。 不幸的是return
无法运行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
传递给脚本,为此您需要使用{{} 1}}如最后一点所示。)
因此,除了一些极端情况之外,只有在以通常的方式执行脚本时才会调用bash
。 通常这就是你想要的,,特别是因为它缺乏复杂难以理解的代码。
请注意,它与Python代码非常相似:
$0
除了某些极端情况外,还可以阻止调用
-c
您可以导入/加载脚本并强制执行main
如果您有可以由多个shell获取的内容,则必须兼容。但是(阅读其他答案),因为没有(易于实现)便携式方法来检测if __name__ == '__main__': main()
,您应该更改规则。
通过强制执行脚本必须由main
执行,您才能完全执行此操作。
这个解决了所有情况,但是,在这种情况下,脚本无法直接运行:
__name__='__main__'
未安装或功能失效(即在引导环境中为E.)source
然而,我无法想到你需要它的任何真正原因,以及能够并行获取完全相同的脚本的能力!通常你可以包装它来手动执行/bin/bash
。像那样:
/bin/bash
curl https://example.com/script | $SHELL
main
$SHELL -c '. script && main'
没有所有其他答案的帮助,这个答案是不可能的!即使是错误的 - 最初让我发布这个。
答案 7 :(得分:5)
我将给出一个BASH特定的答案。 Korn shell,对不起。假设您的脚本名称为include2.sh
;然后在名为include2.sh
的{{1}}中创建内部的函数。这是我的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手册页中查看他们的文档。
只有两点需要注意:
1)对BASH_SOURCE
的调用必须在源代码脚本中进行,但不在任何函数内,以免{ {1}}返回其他内容。是的......你可以检查am_I_called
- 但你只是让你的生活更加艰难。
2)如果要查找所包含文件的名称,函数${FUNCNAME[1]}
必须驻留在源脚本中。
答案 8 :(得分:4)
我想建议对Dennis' very helpful answer进行一次小修正,以使其更具可移植性,我希望:
[ "$_" != "$0" ] && echo "Script is being sourced" || echo "Script is a subshell"
因为(有点肛门保留恕我直言)Debian POSIX兼容 shell [[
无法识别dash
。此外,可能需要引号来防止包含空格的文件名,再次在所述shell中。
答案 9 :(得分:4)
这稍后会在脚本中使用,并且不依赖于_变量:
## 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
答案 10 :(得分:2)
$_
非常脆弱。您必须在脚本中首先检查它。即便如此,也不能保证包含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
。
答案 11 :(得分:1)
我跟着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
随意添加异国情调的贝壳支持:)
答案 12 :(得分:0)
不完全是 OP 想要的,但我经常发现自己需要获取脚本以加载其功能(即作为库)。例如,用于基准测试或测试目的。
这是一个适用于所有 shell(包括 POSIX)的设计
run_main()
函数中--no-run
参数,该参数不执行任何操作; 没有 --no-run
,它可以调用run_main
set -- --no-run "$@"
. script.sh
shift
简单的 .
/ source
的问题是无法将参数可移植地传递给脚本。 POSIX shell 会忽略 .
的参数并无论如何传递调用者的 "$@"
。
答案 13 :(得分:0)
我认为在ksh和bash中都没有任何可移植的方法。在bash中你可以使用caller
输出来检测它,但我认为在ksh中不存在等价物。
答案 14 :(得分:0)
解决此问题的方法不是编写需要知道此类事情才能正确运行的代码。这样做的方法是将代码放入一个函数中,而不是放入需要获取源的脚本的主线中。
函数内的代码可以是 return 0
或 return 1
。这仅终止函数,以便控制返回到调用该函数的任何地方。
无论是从源脚本的主线、顶级脚本的主线还是其他函数调用该函数,这都有效。
使用采购引入“库”脚本,这些脚本只定义函数和变量,但实际上并不执行任何其他顶级命令:
. 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
答案 15 :(得分:0)
这是从其他一些答案中衍生出来的,涉及“通用”跨壳支持。诚然,这与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
只是确保可以将该机制扩展到子进程中。
答案 16 :(得分:0)
我最后检查了[[ $_ == "$(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
,所以我使用{{1 }}以显示bash的完整路径。
测试:
type -p "$0"
答案 17 :(得分:0)
直截了当:您必须评估变量“$ 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
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
答案 18 :(得分:0)
我需要一个可以在[mac,linux]上运行的单行程,使用bash.version&gt; = 3并且这些答案都不符合要求。
[[ ${BASH_SOURCE[0]} = $0 ]] && main "$@"