如何检测脚本是否来源

bri*_*gge 188 bash ksh

我有一个脚本,exit如果它被采购我不希望它调用.

我想过检查是否$0 == bash但如果脚本来自另一个脚本,或者如果用户从不同的shell中获取它,则会出现问题ksh.

是否有可靠的方法来检测脚本是否来源?

小智 152

如果您的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 ..."
Run Code Online (Sandbox Code Playgroud)

  • 这可能是最简洁的方式,因为$ BASH_SOURCE准确地用于此目的. (10认同)
  • 请注意,这不会在ksh下工作,这是OP指定的条件. (4认同)
  • `BASH_SOURCE`是一个数组变量(参见[manual](https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html)),它包含源栈的堆栈跟踪,其中`$ { BASH_SOURCE [0]}`是最新的.这里使用大括号来告诉bash什么是变量名称的一部分.在这种情况下,它们不是"$ 0"所必需的,但它们也不会受到伤害.;) (4认同)
  • 是否有理由使用 `${BASH_SOURCE[0]}` 而不仅仅是 `$BASH_SOURCE`?“${0}”与“$0”? (3认同)
  • @Konrad,如果扩展`$ array`,默认情况下会得到`$ {array [0]}`.那么,有没有理由[...]? (3认同)
  • @CharlesDuffy 我能想到的唯一原因是 `$array == ${array[0]}` 是一种奇怪且意想不到的特殊语法。不熟悉 bash 变量扩展这一事实的读者可能会对不太明确的语法感到困惑。 (3认同)
  • 感谢“less -p <pattern>”的使用——我错过了一个非常有用的选项。 (2认同)

mkl*_*nt0 101

对于强大的解决方案bash,ksh,zsh,包括跨壳一个,再加上合理稳健的POSIX兼容的解决方案:

  • 给出的版本号是验证功能的版本号- 可能这些解决方案也适用于更早版本的版本 - 欢迎反馈.

  • 使用POSIX功能(例如in dash,其作用 /bin/sh于Ubuntu),没有可靠的方法来确定脚本是否来源 - 请参阅下面的最佳近似.

单线跟随 - 下面解释; 跨shell版本很复杂,但它应该可靠地运行:

  • bash(在3.57和4.4.19上验证)

    (return 0 2>/dev/null) && sourced=1 || sourced=0
    
    Run Code Online (Sandbox Code Playgroud)
  • ksh(93u +验证)

    [[ $(cd "$(dirname -- "$0")" && 
       printf '%s' "${PWD%/}/")$(basename -- "$0") != "${.sh.file}" ]] &&
         sourced=1 || sourced=0
    
    Run Code Online (Sandbox Code Playgroud)
  • zsh(在5.0.5上验证) - 务必在函数外部调用它

    [[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0
    
    Run Code Online (Sandbox Code Playgroud)
  • 跨壳(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
    
    Run Code Online (Sandbox Code Playgroud)
  • 符合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
    
    Run Code Online (Sandbox Code Playgroud)

说明:


庆典

(return 0 2>/dev/null) && sourced=1 || sourced=0
Run Code Online (Sandbox Code Playgroud)

注意:该技术改编自user5754163的答案,因为它证明比原始解决方案更强大,[[ $0 != "$BASH_SOURCE" ]] && sourced=1 || sourced=0[1]

  • Bash return仅允许来自函数的语句,并且在脚本的顶级范围内,仅在脚本来源时才允许.

    • 如果return非源脚本的顶级范围中使用,则会发出错误消息,并将退出代码设置为1.
  • (return 0 2>/dev/null)return子shell中执行并抑制错误消息; 之后退出代码指示脚本是sourced(0)还是not(1),它与&&||运算符一起使用以相应地设置sourced变量.

    • 使用子shell是必要的,因为return在源脚本的顶级范围内执行将退出脚本.
    • 在帽子的提示@Haozhun,谁做的命令通过显式使用更稳健0return操作; 他指出:每个bash帮助return [N]:"如果省略N,则返回状态是最后一个命令的状态." 因此,return如果用户shell上的最后一个命令具有非零返回值,则早期版本[仅使用,没有操作数]会产生不正确的结果.

KSH

[[ \
   $(cd "$(dirname -- "$0")" && printf '%s' "${PWD%/}/")$(basename -- "$0") != \
   "${.sh.file}" \
]] && 
sourced=1 || sourced=0
Run Code Online (Sandbox Code Playgroud)

特殊变量${.sh.file}有点类似于$BASH_SOURCE; 请注意,在bash,zsh和dash 中${.sh.file}会导致语法错误,因此请确保在多shell脚本中有条件地执行它.

不像在bash,$0${.sh.file}不能保证是准确的非采购情况相同,因为$0可能是一个相对路径,而${.sh.file}始终是一个完整的路径,所以$0在比较之前必须解决的完整路径.


zsh的

[[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0
Run Code Online (Sandbox Code Playgroud)

$ZSH_EVAL_CONTEXT包含有关评估上下文的信息 - 在函数外部调用它.在源脚本[的顶级范围]内,以.$ZSH_EVAL_CONTEXT 结尾:file.

警告:内部命令替换,zsh的追加:cmdsubst,所以测试$ZSH_EVAL_CONTEXT:file:cmdsubst$存在.


仅使用POSIX功能

如果您愿意做出某些假设,那么基于知道可能正在执行脚本的shell 的二进制文件名,您可以做出合理的,但不是万无一失的猜测,即您的脚本是否来源. 值得注意的是,这意味着如果您的脚本是由另一个脚本提供的,则此方法将失败.

我在这个答案中的"如何处理源调用"一节讨论了仅使用POSIX功能无法详细处理的边缘情况.

依赖于标准的行为$0,其中zsh,例如没有表现.

因此,最安全的方法是将上面的强大的shell特定方法与所有剩余shell 回退解决方案相结合.

StéphaneDesneux的提示以及对灵感的回答(将我的跨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
Run Code Online (Sandbox Code Playgroud)

[1] user1902689发现,[[ $0 != "$BASH_SOURCE" ]]当你执行一个脚本产生假阳性定位于$PATH通过将其仅仅是文件名bash二进制文件; 例如bash my-script,因为$0是那么就my-script,而$BASH_SOURCE完整路径.虽然你通常不会使用这一技术在调用的脚本$PATH-你只是调用它们直接(my-script) -它在与结合有用-x调试.

  • 感谢如此全面的回答。 (4认同)
  • 好吧,我会添加我的答案,因为我很难理解为什么该功能对我不起作用。 (2认同)

F. *_*uri 69

阅读@ DennisWilliamson的回答后,有一些问题,见下文:

由于这个问题代表 ,这个答案中还有另一部分关于 ......见下文.

简单的方式

[ "$0" = "$BASH_SOURCE" ]
Run Code Online (Sandbox Code Playgroud)

让我们尝试(因为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)
Run Code Online (Sandbox Code Playgroud)

我使用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)
Run Code Online (Sandbox Code Playgroud)

请注意,在处理来源时,流程编号不会更改:

echo $$
29301
Run Code Online (Sandbox Code Playgroud)

为什么不使用$_ == $0比较

为了确保很多情况,我开始写一个真正的脚本:

#!/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)"
Run Code Online (Sandbox Code Playgroud)

将其复制到名为的文件testscript:

cat >testscript   
chmod +x testscript
Run Code Online (Sandbox Code Playgroud)

现在我们可以测试:

./testscript 
proc: 25758[ppid:24890] is own (DW purpose: subshell)
Run Code Online (Sandbox Code Playgroud)

没关系.

. ./testscript 
proc: 24890[ppid:24885] is sourced (DW purpose: sourced)

source ./testscript 
proc: 24890[ppid:24885] is sourced (DW purpose: sourced)
Run Code Online (Sandbox Code Playgroud)

没关系.

但是,在添加-x标志之前测试脚本:

bash ./testscript 
proc: 25776[ppid:24890] is own (DW purpose: sourced)
Run Code Online (Sandbox Code Playgroud)

或者使用预定义的变量:

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)
Run Code Online (Sandbox Code Playgroud)

这将不再有效.

将评论从第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
Run Code Online (Sandbox Code Playgroud)

哈德:现在......

由于我没有使用,在手册上阅读了一些内容之后,我有尝试:

#!/bin/ksh

set >/tmp/ksh-$$.log
Run Code Online (Sandbox Code Playgroud)

将其复制到testfile.ksh:

cat >testfile.ksh
chmod +x testfile.ksh
Run Code Online (Sandbox Code Playgroud)

比跑两次:

./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
Run Code Online (Sandbox Code Playgroud)

并看到:

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
Run Code Online (Sandbox Code Playgroud)

源代码运行中有一些变量,但没有真正相关的......

你甚至可以检查$SECONDS是否接近0.000,但这确保只有手动来源的情况......

你甚至可以尝试检查父母是什么:

把它放到你的testfile.ksh:

ps $PPID
Run Code Online (Sandbox Code Playgroud)

比:

./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
Run Code Online (Sandbox Code Playgroud)

或者ps ho cmd $PPID,但这只适用于一个级别的子会话......

对不起,我在下找不到一个可靠的方法.

  • 请注意,`.` 不是`source` 的别名,它实际上是相反的。`source somescript.sh` 是 Bash 主义,不可移植,`。somescript.sh` 是 POSIX 和可移植的 IIRC。 (3认同)

Pau*_*ce. 62

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

[[ $_ != $0 ]] && echo "Script is being sourced" || echo "Script is a subshell"
Run Code Online (Sandbox Code Playgroud)

与此类似的行或类似的赋值pathname="$_"(以及稍后的测试和操作)必须位于脚本的第一行或shebang之后的行(如果使用的话,应该是ksh以便它可以在大多数情况).

  • 如果你使用bash来执行脚本,这也行不通,例如$ bash script.sh然后$ _将是/ bin/bash而不是./script.sh,这是你期望的,当调用脚本时这样:$ ./script.sh无论如何用`$ _`检测都是个问题. (27认同)
  • 不幸的是,它不能保证工作.如果用户设置了'BASH_ENV`,则脚本顶部的`$ _`将是从BASH_ENV`运行的最后一个命令. (9认同)
  • 不幸的是,那是错的!见[我的回答](http://stackoverflow.com/a/23009039/1765658) (8认同)
  • 总结一下:虽然这种方法_typically_工作,但是_not robust_; 它在以下两种情况下失败:(a)`bash script`(通过shell可执行文件调用,该解决方案错误地报告为_sourced_),以及(b)(更不可能)`echo bash; .script`(如果`$ _`恰好匹配源脚本的shell,这个解决方案误报为_subshel​​l_).只有_shell特有的特殊变量(例如,`$ BASH_SOURCE`)才能提供强大的解决方案(因此没有强大的POSIX兼容解决方案).虽然很麻烦,但是可以进行强大的跨壳测试. (8认同)
  • 可以包括其他测试来检查那些调用方法。 (2认同)

mr.*_*tic 31

BASH_SOURCE[]答案(bash的-3.0和更高版本)似乎最简单的,虽然BASH_SOURCE[]未记录到函数体外面工作(它目前发生在工作中,不同意该男子页).

正如Wirawan Purwanto所建议的那样,最强大的方法是FUNCNAME[1] 在函数内检查:

function mycheck() { declare -p FUNCNAME; }
mycheck
Run Code Online (Sandbox Code Playgroud)

然后:

$ bash sourcetest.sh
declare -a FUNCNAME='([0]="mycheck" [1]="main")'
$ . sourcetest.sh
declare -a FUNCNAME='([0]="mycheck" [1]="source")'
Run Code Online (Sandbox Code Playgroud)

这相当于检查caller值的输出mainsource区分调用者的上下文.使用FUNCNAME[]保存捕获和解析caller输出.您需要知道或计算您的本地呼叫深度才是正确的.像从另一个函数或脚本中获取脚本的情况将导致数组(堆栈)更深.(FUNCNAME是一个特殊的bash数组变量,它应该具有与调用堆栈相对应的连续索引,只要它永远不会unset.)

function issourced() {
    [[ ${FUNCNAME[@]: -1} == "source" ]]
}
Run Code Online (Sandbox Code Playgroud)

(在bash-4.2及更高版本中,您可以使用更简单的形式${FUNCNAME[-1]}代替数组中的最后一项.由于Dennis Williamson的评论如下,改进和简化.)

但是,你说的问题是" 我有一个脚本,如果它被采购,我不希望它叫'退出' ".bash这种情况的常见习语是:

return 2>/dev/null || exit
Run Code Online (Sandbox Code Playgroud)

如果脚本是源,那么return将终止源脚本并返回给调用者.

如果正在执行脚本,return则会返回错误(重定向),并将exit正常终止脚本.双方returnexit可以采取的退出代码,如果需要的话.

遗憾的是,这不起作用ksh(至少在我在这里的AT&T派生版本中没有),它被视为return等同于exit在函数或点源脚本之外调用.

更新:您可以在当代版本中执行的操作ksh是检查.sh.level设置为函数调用深度的特殊变量.对于调用的脚本,最初将取消设置,对于点源脚本,它将设置为1.

function issourced {
    [[ ${.sh.level} -eq 2 ]]
}

issourced && echo this script is sourced
Run Code Online (Sandbox Code Playgroud)

这不像bash版本那么强大,您必须issourced()在顶级或已知函数深度的文件中调用.

(您可能也对github上的这段代码感兴趣,它使用一个规程ksh函数和一些调试陷阱技巧来模拟bash FUNCNAME数组.)

这里的规范答案:http://mywiki.wooledge.org/BashFAQ/109还提供$-了shell状态的另一个指标(虽然不完美).


笔记:

  • 可以创建名为"main"和"source"的bash函数(覆盖内置函数),这些名称可能会出现,FUNCNAME[]但只要测试该数组中的最后一项,就没有歧义.
  • 我没有一个好的答案pdksh.我能找到的最接近的东西仅适用于pdksh脚本的每个源代码打开一个新的文件描述符(从原始脚本的10开始).几乎肯定不是你想要依赖的东西......


use*_*163 20

编者注:这个答案的解决方案运行稳健,但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
Run Code Online (Sandbox Code Playgroud)

直接执行:

shell-prompt> sh test.sh
output: This script is not sourced.
Run Code Online (Sandbox Code Playgroud)

来源:

shell-prompt> source test.sh
output: This script is sourced.
Run Code Online (Sandbox Code Playgroud)

对我来说,这适用于zsh和bash.

说明

return如果您尝试在函数外部执行它或者未获取脚本,则该语句将引发错误.从shell提示符处尝试:

shell-prompt> return
output: ...can only `return` from a function or sourced script
Run Code Online (Sandbox Code Playgroud)

您不需要查看该错误消息,因此您可以将输出重定向到dev/null:

shell-prompt> return >/dev/null 2>&1
Run Code Online (Sandbox Code Playgroud)

现在检查退出代码.0表示正常(未发生错误),1表示发生错误:

shell-prompt> echo $?
output: 1
Run Code Online (Sandbox Code Playgroud)

您还希望return在子shell中执行语句.当return语句运行它...好 ...回报.如果您在子shell中执行它,它将返回该子shell,而不是返回您的脚本.要在子shell中执行,请将其包装在$(...):

shell-prompt> $(return >/dev/null 2>$1)
Run Code Online (Sandbox Code Playgroud)

现在,你可以看到子shell的退出代码,它应该是1,因为在子shell中引发了一个错误:

shell-prompt> echo $?
output: 1
Run Code Online (Sandbox Code Playgroud)

  • POSIX没有指定什么`return`应该在顶层(http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_24_03)做的.`dash` shell将顶层的`return`视为`exit`.像"bash"或"zsh"这样的其他shell在顶层不允许"返回",这就是这种技术的功能. (2认同)
  • 当在错误退出命令之后对脚本进行“源”处理时,“返回”没有明确的返回值会中断。建议增强编辑。 (2认同)

Tin*_*ino 9

FWIW,在阅读了所有其他答案之后,我想出了以下解决方案:

这适用于所有的脚本,这与启动#!/bin/bash,但可能是由不同的炮弹来采购为好.

#!/bin/bash

# Function definitions (API) and shell variables (constants) go here
# (This is what might be interesting for other shells, too.)

# this main() function is only meant to be meaningful for bash
main()
{
# The script's execution part goes here
}

BASH_SOURCE=".$0" # cannot be changed in bash
test ".$0" != ".$BASH_SOURCE" || main "$@"
Run Code Online (Sandbox Code Playgroud)

此脚本配方具有以下属性:

  • 如果执行main,bash则调用.
  • 如果调用脚本恰好具有相同名称/bin/sh,bash则仅调用source .(例如,如果它自己来源.)
  • 如果由shell以外的其他来源bash,BASH_SOURCE则不会被调用.
  • 如果由shell以外执行set -e,main则不会被调用.
  • 如果通过bashwith main(bash -x script 所有引用很重要!)不直接来自命令行进行评估,则会调用script.对于所有其他变体bash,main未调用.

  • 如果bash -c 'someotherscript "$@"' main-script args..未调用,则返回main-script(test).

  • 它不依赖于可能改变的无证行为.

因此,除了一些不太可能的极端情况之外,$BASH_SOURCE只有当脚本以通常的方式执行时才被调用. 通常这就是你想要的,特别是因为它缺乏复杂难以理解的代码.

由于eval无法取消bash,但在所有其他shell中,这也会捕获main恰好设置为的边缘情况BASH_SOURCE.

请注意,它与Python代码非常相似:

if ( BASH_SOURCE=".$0" && exec test ".$0" != ".$BASH_SOURCE" ); then :; else main "$@"; fi
Run Code Online (Sandbox Code Playgroud)

$0除了某些极端情况之外,这也可以防止调用,因为您可以导入/加载脚本并强制执行该操作main

为什么我认为这是解决挑战的一般方法

如果您有可以由多个shell获取的内容,则必须兼容.但是(阅读其他答案),因为没有可移植的方法来检测bashing,所以必须更改规则.

通过强制执行脚本必须执行$0,您完全执行此操作.

解决了所有情况,但在这种情况下脚本无法直接运行:

  • ( exec -a '' /bin/bash ) <script 没有安装或功能不正常(即.在引导环境中)
  • 如果你把它管道到非bash-shell,就像在bash哪里eval没有eval "`cat script`"

但是,我无法考虑您需要它的任何真正原因,也无法并行获取完全相同的脚本!通常你可以包装它,这样脚本总是来源的.然后main手动执行.像那样:

  • eval
  • BASH_SOURCE

重要:

后者运行$0两次,如果/bin/bash运行main并且该行未从命令行运行.(但我真的想不出为什么你应该在脚本中使用它,除了故意欺骗代码.)

注意

没有所有其他答案的帮助,这个答案是不可能的!即使是错误的 - 这让我发布了这个.


jim*_*ara 5

这将在脚本的更高版本中起作用,并且不依赖于_变量:

## Check to make sure it is not sourced:
Prog=myscript.sh
if [ $(basename $0) = $Prog ]; then
   exit 1  # not sourced
fi
Run Code Online (Sandbox Code Playgroud)

要么

[ $(basename $0) = $Prog ] && exit
Run Code Online (Sandbox Code Playgroud)

  • 我认为这个答案是这里为数不多的符合 POSIX 标准的答案之一。明显的缺点是您必须知道文件名,并且如果两个脚本具有相同的文件名,则它不起作用。 (3认同)

Wir*_*nto 5

我将给出一个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
Run Code Online (Sandbox Code Playgroud)

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

~/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
Run Code Online (Sandbox Code Playgroud)

因此,这无一例外地工作,并且它没有使用脆弱的$_东西.这个技巧使用了BASH的内省设施,即内置变量FUNCNAMEBASH_SOURCE; 在bash手册页中查看他们的文档.

只有两点需要注意:

1)调用am_I_called 必须发生被执行的脚本,但没有在任何功能,以免${FUNCNAME[1]}返回别的东西.是的......你可以检查${FUNCNAME[2]}- 但你只是让你的生活更加艰难.

2)如果要查找所包含文件的名称,函数am_I_called 必须驻留在源脚本中.