在$ 0和BASH_SOURCE之间选择

H2O*_*aCl 84 bash

如何在"$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]}
Run Code Online (Sandbox Code Playgroud)

mkl*_*nt0 137

${BASH_SOURCE[0]}(或者,更简单地说,$BASH_SOURCE[1] )包含所有调用场景中包含脚本的(可能是相对的)路径,特别是在脚本来源时,这是不正确的$0.

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

以下示例说明了这一点:

脚本foo:

#!/bin/bash
echo "[$0] vs. [${BASH_SOURCE[0]}]"
Run Code Online (Sandbox Code Playgroud)
$ bash ./foo
[./foo] vs. [./foo]

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

$ . ./foo
[bash] vs. [./foo]
Run Code Online (Sandbox Code Playgroud)

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


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

击允许引用元件0的的阵列使用可变标量表示法:代替书写${arr[0]},可以编写$arr; 换句话说:如果你引用变量就好像它是一个标量,你就得到了索引元素0.

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

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

旁注:虽然这个警告很有用,但它可能更精确,因为你不一定会得到第一个元素:它特别0是返回索引的元素,所以如果第一个元素有一个更高的索引 -在Bash中是可能的 - 你会得到空字符串; 试试'a[1]='hi'; echo "$a"'.
(相比之下,zsh叛徒,无论其指数如何,确实返回第一个元素).

您可以选择避开这一功能,因为它默默无闻,但它的工作原理可以预见和务实地讲,你会很少,如果有的话,需要访问索引其他0数组变量${BASH_SOURCE[@]}.

  • @AlexanderMills是的,如果您使用的是Bash,`$ BASH_SOURCE`是更好的选择。 (2认同)
  • “(相比之下,zsh,永远的叛徒,确实返回第一个元素,无论其索引是什么)。” 实际上,在 zsh 中,无下标的数组参数扩展成为整个数组,而不仅仅是第一项。如果没有设置 shwordsplit,`$ary` ~= `"${ary[@]}"`、`"$ary"` ~= `"${ary[*]}"` 和 `$=ary` ~=不带引号的“${ary[*]}”或“${ary[@]}”。 (2认同)

it-*_*ien 28

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

(成功)运行:

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

最后:

$ sh t.sh
$0:                    [t.sh]
$BASH_SOURCE:          []
$BASH_SOURCE or $0:    [t.sh]
t.sh: 6: t.sh: Bad substitution
Run Code Online (Sandbox Code Playgroud)

恢复

正如您所看到的,只有第三个变体:${BASH_SOURCE:-$0}- 在所有调用场景下都有效并给出一致的结果。请注意,我们利用bash 的功能来引用等于第一个数组元素的无下标数组变量。

  • 使用无下标数组是一个好主意,但是在 shell 脚本中使用 bash 功能来寻找 POSIX 合规性似乎不是很明智。这意味着根据执行 shell 的不同,您在获取源代码时会有不同的行为,因此似乎最好避免这种情况并在 POSIX“模式”中坚持使用“$0”,不是吗? (2认同)

Huw*_*ers 10

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

$ cat outer.sh
#!/usr/bin/env bash
./middle.sh
Run Code Online (Sandbox Code Playgroud)
$ cat middle.sh
#!/usr/bin/env bash
./inner.sh
Run Code Online (Sandbox Code Playgroud)
$ 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]}'"
Run Code Online (Sandbox Code Playgroud)
$ ./outer.sh
$0 = './inner.sh'
$BASH_SOURCE[0] = './inner.sh'
$BASH_SOURCE[1] = ''
$BASH_SOURCE[2] = ''
Run Code Online (Sandbox Code Playgroud)

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

$ cat outer.sh
#!/usr/bin/env bash
source ./middle.sh
Run Code Online (Sandbox Code Playgroud)
$ cat middle.sh
#!/usr/bin/env bash
source ./inner.sh
Run Code Online (Sandbox Code Playgroud)
$ 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]}'"
Run Code Online (Sandbox Code Playgroud)
$ ./outer.sh
$0 = './outer.sh'
$BASH_SOURCE[0] = './inner.sh'
$BASH_SOURCE[1] = './middle.sh'
$BASH_SOURCE[2] = './outer.sh'
Run Code Online (Sandbox Code Playgroud)


use*_*610 10

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

${BASH_SOURCE[0]:-$0}
Run Code Online (Sandbox Code Playgroud)

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