代码之家  ›  专栏  ›  技术社区  ›  brianegge

如何检测脚本的源代码

  •  162
  • brianegge  · 技术社区  · 15 年前

    我有一个脚本,我不想让它调用 exit 如果它正在被采购。

    我想检查一下 $0 == bash 但是,如果脚本来自另一个脚本,或者用户来自不同的shell,比如 ksh .

    是否有可靠的方法来检测脚本是否正在被获取?

    16 回复  |  直到 6 年前
        1
  •  52
  •   Radagast the Brown Dennis Williamson    6 年前

    这似乎在bash和korn之间是可移植的:

    [[ $_ != $0 ]] && echo "Script is being sourced" || echo "Script is a subshell"
    

    类似于此的行或类似于作业的行 pathname="$_" (稍后进行测试和操作)必须位于脚本的第一行或shebang之后的行(如果使用,则应用于ksh,以便在大多数情况下工作)。

        2
  •  134
  •   barroyo    15 年前

    如果您的bash版本知道bash_源数组变量,请尝试如下操作:

    # 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 ..."
    
        3
  •  79
  •   mklement0    6 年前

    强大的解决方案 bash , ksh , zsh ,包括 十字壳 一加一 相当健壮的符合POSIX的解决方案 :

    • 给出的版本号是功能所在的版本号。 已证实的 -很可能,这些解决方案也适用于更早的版本。- 欢迎反馈 .

    • 使用 仅限POSIX功能 (如在 dash 作为 /bin/sh 在Ubuntu),有 稳健的 方式 要确定是否正在获取脚本,请参阅下面的 近似 .

    单衬垫 请遵循下面的说明;交叉shell版本很复杂,但它应该能够可靠地工作:

    • 猛击 (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
      
    • 十字壳(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
      

    说明:


    猛击

    (返回0 2>/dev/null)&source=1 source=0
    

    注:该技术改编自 user5754163's answer 因为它比原来的解决方案更强大, [[ $0 != "$BASH_SOURCE" ]] && sourced=1 || sourced=0 〔1〕

    • BASH允许 return 语句仅来自函数,并且在脚本的顶级作用域中,仅当脚本是 来源的 .

      • 如果 返回 用于 非来源的 脚本,发出错误消息,退出代码设置为 1 .
    • (return 0 2>/dev/null) 执行 返回 在一个 亚壳层 并抑制错误消息;然后退出代码指示脚本是否源于( 0 )与否 )用于 && || 操作员设置 sourced 相应的变量。

      • 必须使用子外壳,因为执行 返回 在源脚本的顶级范围内,将退出该脚本。
      • 帽子的尖端 @Haozhun ,通过显式地使用 作为 返回 操作数;他注意到:每个bash的帮助 return [N] :“如果省略n,则返回状态为最后一个命令的状态。”因此,早期版本[仅使用 返回 ,没有操作数] 如果用户shell上的最后一个命令具有非零返回值,则会产生不正确的结果。

    KSH

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

    特殊变量 ${.sh.file} 有点类似于 $BASH_SOURCE 注意 ${.S.Frase} 原因A 语法错误 在bash、zsh和dash中,一定要执行它 有条件地 在多shell脚本中。

    不像巴什, $0 ${.S.Frase} 不保证 确切地 在非来源案例中相同,如 0美元 可能是 相对的 路径,而 ${.S.Frase} 永远是 满的 路径,所以 0美元 在比较之前必须解析为完整路径。


    ZSH

    [$zsh_eval_context=~:文件$]&&source=1 source=0
    

    $ZSH_EVAL_CONTEXT 包含有关计算上下文的信息-在函数外部调用它。在源脚本['s top-level scope]中, $zsh评估上下文 末端 具有 :file .

    警告:在命令替换中,zsh附加 :cmdsubst 所以测试 $zsh评估上下文 对于 :file:cmdsubst$ 那里。


    仅使用POSIX功能

    如果你愿意做出某些假设,你可以 做一个 合理,但不是傻瓜的猜测 至于脚本的来源,基于 知道二进制文件名 可能正在执行脚本的shell .
    值得注意的是,这意味着如果脚本的源代码是 另一脚本 .

    中的“如何处理源调用”部分 this answer 我讨论的边缘案例 不能 仅在细节上使用POSIX功能处理。

    这个 依靠的标准行为 0美元 哪一个 ZSH ,例如是 展览。

    因此,最安全的方法是 将上述健壮的、特定于shell的方法与 回退解决方案 所有剩余外壳。

    帽子的尖端 Stéphane Desneux his answer 为了灵感(将我的跨壳陈述表达转化为 sh 相容的 if 语句并为其他shell添加处理程序)。

    来源= 0
    如果[-n“$zsh_eval_context”];则
    案例$zsh_eval_context in*:file)sourced=1;;esac
    elif[-n“$ksh_版本”];然后
    [“$(cd$(dirname--$0)&&pwd-p)/$(basename--$0)”!=“$(cd$(dirname--$.sh.file)&&pwd-p)/$(basename--$.sh.file)”]&&source=1
    elif[-n“$bash_版本”];然后
    (返回0 2>/dev/null)&source=1
    else所有其他shell:检查$0以获取已知的shell二进制文件名
    #检测“sh”和“dash”;根据需要添加其他shell文件名。
    案例0*/在sh破折号中)来源=1;;ESAC
    FI
    

    〔1〕 user1902689 发现 [[ $0 != "$BASH_SOURCE" ]] 在执行脚本时生成误报 位于 $PATH 通过它 仅文件名 猛击 二进制的;例如 bash my-script ,因为 0美元 就这样 my-script 巴沙源 全路径 . 虽然您通常不会使用此技术在 $路径 -你只要调用它们 直接地 ( 我的剧本 -它 当与 -x 对于 调试 .

        4
  •  66
  •   brian F. Hauri - Give Up GitHub    8 年前

    在阅读了@denniswilliamson的答案后,有一些问题,请参阅以下内容:

    因为这个问题代表 ,在这个答案中还有另一部分涉及 …见下文。

    简单的 方式

    [ "$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 取而代之 . 可读性(如 . 是一个别名 来源 ):

    . <(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 比较

    为了确保多个案例,我开始写 脚本:

    #!/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
    

    更努力: 现在。。。

    因为我不使用 很多,在阅读了一些手册之后,我尝试了一下:

    #!/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 但这只能保证 手工来源 病例…

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

    把这个放到你的 测试文件 :

    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 但这只适用于一个级别的子会话…

    对不起,我找不到一个可靠的方法来做那件事。 .

        5
  •  30
  •   Community CDub    8 年前

    这个 BASH_SOURCE[] 答案(bash-3.0和更高版本)似乎最简单 巴什源 未记录在职能机构之外工作 (与手册页不一致,目前它正好起作用)。

    正如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[] 保存捕获和分析 呼叫者 输出。不过,您需要知道或计算您的本地呼叫深度才能正确。例如脚本来自另一个函数或脚本的情况将导致数组(堆栈)更深。( FUNCNAME 是一个特殊的bash数组变量,它应该有与调用堆栈对应的连续索引,只要它从不 unset )

    function issourced() {
        [[ ${FUNCNAME[@]: -1} == "source" ]]
    }
    

    (在bash-4.2及更高版本中,您可以使用更简单的形式 ${FUNCNAME[-1]} 而不是数组中的最后一个项。感谢丹尼斯·威廉姆森在下面的评论,改进和简化了它。)

    但是,你的问题是” 我有一个脚本,我不希望它在源代码中调用“exit” “。共同点 bash 这种情况的成语是:

    return 2>/dev/null || exit
    

    如果脚本是源代码,那么 return 将终止源脚本并返回调用方。

    如果正在执行脚本,则 返回 将返回一个错误(重定向),并且 exit 将正常终止脚本。两个 返回 出口 如果需要,可以使用退出代码。

    可悲的是,这不适用于 ksh (至少在我这里的AT&T派生版本中没有),它处理 返回 相当于 出口 如果在函数或点源脚本外部调用。

    更新的 你怎么了 可以 在当代版本的 KSH 是检查特殊变量 .sh.level 设置为函数调用深度。对于被调用的脚本,这一点最初将被取消设置,对于点源脚本,它将被设置为1。

    function issourced {
        [[ ${.sh.level} -eq 2 ]]
    }
    
    issourced && echo this script is sourced
    

    这不像bash版本那么健壮,必须调用 issourced() 在文件中,您从顶层或已知函数深度测试。

    (您也可能对 this code 在使用 KSH 规程功能和一些调试陷阱技巧来模拟bash 函数名 数组)

    这里的标准答案是: http://mywiki.wooledge.org/BashFAQ/109 还提供 $- 作为外壳状态的另一个指标(尽管不完美)。


    笔记:

    • 可以创建名为“main”和“source”的bash函数( overriding the builtin ,这些名称可能出现在 函数名[ ] 但是,只要只测试该数组中的最后一个项,就不会有歧义。
    • 我没有很好的答案 pdksh . 我能找到的最接近的东西只适用于 PDKSH ,其中脚本的每个源代码都会打开一个新的文件描述符(对于原始脚本,从10开始)。几乎肯定不是你想依靠的东西…
        6
  •  18
  •   mklement0    6 年前

    编者按:这个答案的解决方案很有效,但是 bash -只。它可以简化为
    (return 2>/dev/null) .

    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中有效。

    解释

    这个 返回 如果您试图在函数之外执行语句,或者脚本没有源代码,则该语句将引发错误。从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内。当 返回 语句运行它。…好。…返回。如果在子shell中执行它,它将从该子shell返回,而不是从脚本返回。要在子shell中执行,请将其包装在 $(...) :

    shell-prompt> $(return >/dev/null 2>$1)
    

    现在,您可以看到子shell的退出代码,应该是1,因为子shell中出现了错误:

    shell提示>echo$?
    产量:1
    
        7
  •  6
  •   Tino    7 年前

    在阅读了所有其他答案之后,我为自己提出了以下解决方案:

    这适用于所有脚本,其中 从开始 #!/bin/bash 但也可能是由不同的外壳提供的。

    #!/bin/bash
    
    # Function definitions (API) and shell variables (constants) go here
    
    main()
    {
    # The script's execution part goes here
    }
    
    unset BASH_SOURCE 2>/dev/null
    test ".$0" != ".$BASH_SOURCE" || main "$@"
    

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

    • 如果被执行 bash , main 被称为。
    • 如果来源于 猛击 , 主要的 仅当调用脚本恰好具有相同的名称时才调用。(例如,如果它自己发出信号。)
    • 如果来源于除 猛击 , 主要的 不被调用。
    • 如果由除 猛击 , 主要的 不被调用。
    • 如果评估 猛击 具有 eval ( eval "`cat script`" 所有报价都很重要! )不是直接从命令行来的,这个调用 主要的 . 所有其他变体 埃瓦 , 主要的 不被调用。

    • 如果 主要的 不被调用,它不会返回 true ( $?=0 )

    • 而且它不依赖可能改变的非法行为。

    因此,除了一些不太可能发生的角落案件, 主要的 仅当以常规方式执行脚本时调用。 通常这是你想要的, 尤其是因为它缺乏复杂的难以理解的代码。

    AS BASH_SOURCE 不能在中取消设置 猛击 但在所有其他外壳中,这也会捕捉到 巴什源 恰好被设置为 $0 .

    请注意,它非常类似于python代码:

    if __name__ == '__main__': main()
    

    这也阻止了 主要的 ,除了一些角箱,如 您可以导入/加载脚本并强制执行该脚本 __name__='__main__'

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

    如果您有可以通过多个shell获得的东西,那么它必须是兼容的。但是(阅读其他答案) 因为没有可移植的方法来检测 source 英,你必须改变规则 .

    通过强制脚本必须由 /bin/bash ,你就是这么做的。

    这个 解决除以下情况以外的所有情况 在这种情况下,脚本不能直接运行:

    • /BI/BASH 未安装或功能不正常(即在引导环境中)
    • 如果你用管道把它输送到一个非bash外壳上 curl https://example.com/script | $SHELL 哪里 $SHELL 不是 猛击

    然而,我不能想到任何真正的原因,在您需要的地方,也能够源代码完全相同的脚本并行!通常可以包装它,这样脚本总是源代码。然后执行 主要的 用手。像这样:

    • sh -c '. script && main'
    • echo 'eval "`curl https://example.com/script`" && main' | $SHELL

    重要:

    后者运行 主要的 两次,如果 贝壳 猛击 并且该行不是从命令行运行的。(但我真的想不出任何理由为什么你应该在脚本中使用它,除了故意愚弄代码之外。)

    注释

    如果没有其他答案的帮助,这个答案是不可能的!甚至是错误的——这让我把它贴了出来。

        8
  •  5
  •   Wirawan Purwanto danodonovan    12 年前

    我会给出一个具体的回答。Korn Shell,对不起。假设您的脚本名是 include2.sh ;然后生成函数 里面 这个 包含2SH 打电话 am_I_sourced . 这是我的演示版 包含2SH :

    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)函数 阿米伊尔 必须 如果您想知道所包含的文件名,请驻留在源脚本中。

        9
  •  4
  •   Community CDub    8 年前

    我建议对 Dennis' very helpful answer 为了使它更轻便,我希望:

    [ "$_" != "$0" ] && echo "Script is being sourced" || echo "Script is a subshell"
    

    因为 [[ 不被Debian识别(有点保留肛门) POSIX兼容 壳牌, dash . 此外,在所述shell中,可能还需要引号来防止文件名包含空格。

        10
  •  3
  •   jim mcnamara    15 年前

    这在后面的脚本中起作用,不依赖于变量:

    ## 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
    
        11
  •  2
  •   Mikel    14 年前

    $_ 很脆。作为脚本中的第一件事,您必须检查它。即使这样,也不能保证包含shell的名称(如果源代码)或脚本的名称(如果执行)。

    例如,如果用户设置了 BASH_ENV ,然后在脚本的顶部, 美元 包含在中执行的最后一个命令的名称 巴什耶涅夫 脚本。

    我找到的最好方法是 $0 这样地:

    name="myscript.sh"
    
    main()
    {
        echo "Script was executed, running main..."
    }
    
    case "$0" in *$name)
        main "$@"
        ;;
    esac
    

    不幸的是,这种方法在zsh中不适用,因为 functionargzero 选项的作用超出了其名称所建议的范围,并且在默认情况下处于启用状态。

    为了解决这个问题,我把 unsetopt functionargzero 在我的 .zshenv .

        12
  •  1
  •   Community CDub    8 年前

    我跟随 mklement0 compact expression .

    这很好,但我注意到,当调用ksh时,它可能会失败:

    /bin/ksh -c ./myscript.sh
    

    (它认为它是源代码,而不是因为它执行了子外壳) 但是这个表达式可以检测到:

    /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
    

    请随意添加异域贝壳支持:)

        13
  •  0
  •   Michal Čihař    15 年前

    我认为在ksh和bash中没有任何可移植的方法来实现这一点。在bash中,您可以使用 caller 输出,但我不认为在ksh中存在等价物。

        14
  •  0
  •   Karsten    10 年前

    我需要一个在bash.version>=3的[mac,linux]上工作的一行程序,这些答案都不符合要求。

    [[ ${BASH_SOURCE[0]} = $0 ]] && main "$@"
    
        15
  •  0
  •   ivanleoncz velhala    8 年前

    直奔主题: 必须计算变量 “0美元” 等于外壳的名称。


    这样地:

    #!/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%便携式 检测脚本是否来源。

    关于我的经验 (使用shell脚本编写7年) ,唯一安全的方法(不依赖环境变量 PID 等等,这是不安全的,因为它是 变量 你应该:

    • 扩大你的国际单项体育联合会的可能性
    • 如果愿意,可以使用开关/外壳。

    这两个选项都不能自动缩放,但这是更安全的方法。



    例如:

    通过ssh会话源代码脚本时,变量返回的值 “0美元” (使用时) 来源 -巴什 .

    #!/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
    
        16
  •  0
  •   osexp2000    7 年前

    最后我检查了一下 [[ $_ == "$(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