【问题标题】:choosing between $0 and BASH_SOURCE在 $0 和 BASH_SOURCE 之间选择
【发布时间】:2016-05-02 13:55:39
【问题描述】:

如何在"$0""${BASH_SOURCE[0]}" 之间进行选择

来自GNU 的描述对我没有多大帮助。

    BASH_SOURCE

 An array variable whose members are the source filenames where the
 corresponding shell function names in the FUNCNAME array variable are
 defined. The shell function ${FUNCNAME[$i]} is defined in the file
 ${BASH_SOURCE[$i]} and called from ${BASH_SOURCE[$i+1]}

【问题讨论】:

  • BASH_SOURCE 在 bash-3.0-alpha 中添加。根据您的测试制度,您可能没有它。我发现它在早期的 Solaris 和 OS X 上都丢失了。另请参阅 U&L.SE 上的return: can only `return' from a function or sourced script
  • 还有其他人认为An array variable whose members are the source filenames where the corresponding shell function names in the FUNCNAME array variable are defined. The shell function ${FUNCNAME[$i]} is defined in the file ${BASH_SOURCE[$i]} and called from ${BASH_SOURCE[$i+1]} 完全是胡言乱语吗?我不知道这意味着什么......如果有人解释一下文档/手册页实际上想说什么,那就太好了。
  • 当我意识到文档描述的是构建堆栈跟踪所需的点点滴滴时,这些文档更有意义。 Bash 本身没有堆栈跟踪库(我可以找到)。相反,我不得不使用 FUNCNAME 和 BASH_SOURCE 来构建我自己的......

标签: bash


【解决方案1】:

TL;DR我建议使用 ${BASH_SOURCE:-$0} 作为最通用的变体。

以前的答案很好,但没有提到直接使用${BASH_SOURCE[0]} 的一个警告:如果您将脚本作为sh 的参数调用并且您的sh 没有别名为bash(在我的情况下,在 Ubuntu 16.04.5 LTS 上,它链接到 dash),它可能会失败,BASH_SOURCE 变量为空/未定义。这是一个例子:

t.sh:

#!/usr/bin/env bash

echo "\$0:                    [$0]"
echo "\$BASH_SOURCE:          [$BASH_SOURCE]"
echo "\$BASH_SOURCE or \$0:    [${BASH_SOURCE:-$0}]"
echo "\$BASH_SOURCE[0] or \$0: [${BASH_SOURCE[0]:-$0}]"

(成功)运行:

$ ./t.sh
$0:                    [./t.sh]
$BASH_SOURCE:          [./t.sh]
$BASH_SOURCE or $0:    [./t.sh]
$BASH_SOURCE[0] or $0: [./t.sh]

$ source ./t.sh
$0:                    [/bin/bash]
$BASH_SOURCE:          [./t.sh]
$BASH_SOURCE or $0:    [./t.sh]
$BASH_SOURCE[0] or $0: [./t.sh]

$ bash t.sh
$0:                    [t.sh]
$BASH_SOURCE:          [t.sh]
$BASH_SOURCE or $0:    [t.sh]
$BASH_SOURCE[0] or $0: [t.sh]

最后:

$ sh t.sh
$0:                    [t.sh]
$BASH_SOURCE:          []
$BASH_SOURCE or $0:    [t.sh]
t.sh: 6: t.sh: Bad substitution

简历

如您所见,只有第三种变体:${BASH_SOURCE:-$0} - 在所有调用场景下都有效并给出一致的结果。请注意,我们利用bash's feature 对等于第一个数组元素的无下标数组变量进行引用。

【讨论】:

  • 使用无下标数组是个好主意,但是在 shell 脚本中使用 bash 功能来寻找 POSIX 合规性似乎不是很明智。这意味着根据执行的 shell 的不同,您在获取时会有不同的行为,因此在这里最好避免这种情况并在 POSIX 'mode' 中坚持使用$0,不是吗?
  • @Zilog80 实际上,我并不是在寻求 POSIX 合规性,有 another answer 来解决它,我只是提供了使它在最可能(据我所知)情况下工作的方法,而不是关心/bin/sh 下实际上隐藏了什么shell 方言(不过,希望它会是bash :)
【解决方案2】:

注意:对于 POSIX 兼容 解决方案,请参阅this answer

${BASH_SOURCE[0]}(或者,更简单地说,$BASH_SOURCE[1] ) 包含 all 调用场景中包含脚本的(可能是相对的)路径,尤其是当脚本是 sourced 时,$0 并非如此。 p>

此外,正如Charles Duffy 指出的那样,调用者可以将$0 设置为任意 值。
另一方面,如果不涉及命名文件,$BASH_SOURCE 可以为空;例如:
echo 'echo "[$BASH_SOURCE]"' | bash

以下示例说明了这一点:

脚本foo:

#!/bin/bash
echo "[$0] vs. [${BASH_SOURCE[0]}]"

$ bash ./foo
[./foo] vs. [./foo]

$ ./foo
[./foo] vs. [./foo]

$ . ./foo
[bash] vs. [./foo]

$0 是 POSIX shell 规范的一部分,而BASH_SOURCE,顾名思义,是特定于 Bash 的。


[1] 可选阅读:${BASH_SOURCE[0]} vs. $BASH_SOURCE

Bash 允许您使用 标量 表示法来引用 array 变量的元素 0:您可以写 ${arr[0]},而不是写 ${arr[0]};换句话说:如果你引用变量就好像它是一个标量,你会得到索引0的元素。

使用此功能掩盖了 $arr 是一个数组这一事实,这就是流行的 shell 代码 linter shellcheck.net 发出以下警告的原因(截至本文撰写时):

SC2128:扩展没有索引的数组只会给出第一个元素。

附带说明:虽然此警告很有帮助,但它可能更准确,因为您不一定会获得 first 元素:它特别是索引 0 处的元素返回,所以如果第一个元素有更高的索引——这在 Bash 中是可能的——你会得到空字符串;试试'a[1]='hi'; echo "$a"'
(相比之下,zsh,曾经的叛徒,确实确实返回第一个元素,而不管它的索引)。

由于其晦涩难懂,您可能会选择避开此功能,但它的工作原理是可预测的,而且从实用的角度讲,您几乎不需要访问数组的0 以外的索引其他变量${BASH_SOURCE[@]}


可选阅读,第 2 部分:在什么情况下,BASH_SOURCE 数组变量实际上包含多个元素?

BASH_SOURCE 仅具有多个条目如果涉及函数调用,在这种情况下,其元素与包含所有函数名称的 FUNCNAME 数组平行 >.

也就是说,在函数内部,${FUNCNAME[0]} 包含执行函数的名称,${BASH_SOURCE[0]} 包含定义该函数的脚本文件的路径,${FUNCNAME[1]} 包含从中执行的函数的名称当前执行的函数被调用(如果适用),依此类推。

如果给定函数直接从脚本文件的顶级范围调用,该脚本文件在调用堆栈的级别 $i 定义了函数,${FUNCNAME[$i+1]} 包含:

  • main(伪函数名称),如果脚本文件被直接调用(例如,./script

  • source(伪函数名称),如果脚本文件是来源(例如source ./script. ./script)。

【讨论】:

  • 所以 $BASH_SOURCE 更通用,适用于更多情况?
  • @AlexanderMills 是的,如果您使用 Bash,$BASH_SOURCE 是更好的选择。
  • @CharlieParker,请查看我添加到答案中的新底部部分。
【解决方案3】:

为了可移植性,定义时使用${BASH_SOURCE[0]},否则使用$0。这给了

${BASH_SOURCE[0]:-$0}

值得注意的是,在 zsh 中,$0 确实包含正确的文件路径,即使脚本是 sourced。

【讨论】:

    【解决方案4】:

    这些脚本可能有助于说明。外部脚本调用中间脚本,中间脚本调用内部脚本:

    $ cat outer.sh
    #!/usr/bin/env bash
    ./middle.sh
    
    $ cat middle.sh
    #!/usr/bin/env bash
    ./inner.sh
    
    $ cat inner.sh
    #!/usr/bin/env bash
    echo "\$0 = '$0'"
    echo "\${BASH_SOURCE[0]} = '${BASH_SOURCE[0]}'"
    echo "\${BASH_SOURCE[1]} = '${BASH_SOURCE[1]}'"
    echo "\${BASH_SOURCE[2]} = '${BASH_SOURCE[2]}'"
    
    $ ./outer.sh
    $0 = './inner.sh'
    $BASH_SOURCE[0] = './inner.sh'
    $BASH_SOURCE[1] = ''
    $BASH_SOURCE[2] = ''
    

    但是,如果我们将脚本调用更改为 source 语句:

    $ cat outer.sh
    #!/usr/bin/env bash
    source ./middle.sh
    
    $ cat middle.sh
    #!/usr/bin/env bash
    source ./inner.sh
    
    $ cat inner.sh
    #!/usr/bin/env bash
    echo "\$0 = '$0'"
    echo "\${BASH_SOURCE[0]} = '${BASH_SOURCE[0]}'"
    echo "\${BASH_SOURCE[1]} = '${BASH_SOURCE[1]}'"
    echo "\${BASH_SOURCE[2]} = '${BASH_SOURCE[2]}'"
    
    $ ./outer.sh
    $0 = './outer.sh'
    $BASH_SOURCE[0] = './inner.sh'
    $BASH_SOURCE[1] = './middle.sh'
    $BASH_SOURCE[2] = './outer.sh'
    

    【讨论】:

      猜你喜欢
      • 2023-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-25
      • 2011-01-29
      • 2010-12-06
      • 2012-04-13
      • 2011-11-15
      相关资源
      最近更新 更多