zsh中的${BASH_SOURCE[0]}等价物是什么?

90
标题已经说明了一切。我正在寻找与 Bash 中的 ${BASH_SOURCE[0]} 相当的 Zsh 等效命令。
注意:我在互联网上一直发现“$0 等同于 ${BASH_SOURCE[0]}”,但这似乎是错误的:$0 似乎是正在执行的命令的名称。(它是 argv[0],这很有道理。)在我的脚本(.zshrc)中回显 $0 给出 zsh,这与 ${BASH_SOURCE[0]} 不同。实际上,${BASH_SOURCE[0]}zsh 中运行得很好,除了在 .zshrc 文件内部。
我在我的 .zshrc 中真正要做的事情(但无法正常工作):
echo ${BASH_SOURCE[0]}
source `dirname $0`/common-shell-rc.sh

源代码失败($0zsh),echo 输出空白行。

编辑: 显然,要让 $0 正常工作,我需要设置 FUNCTION_ARGZERO 选项。有没有方法在脚本中测试是否设置了此选项?(这样我就可以暂时设置它)除非你设置了 nofunction_argzero,否则它似乎默认是打开的,并且在我的 shell 中也是打开的。但是对于 $0,仍然没有输出。(我想是因为我不在函数中。)

7个回答

75
在zsh中,${BASH_SOURCE[0]}的等价物是${(%):-%N},而不是$0(正如OP所说,后者在.zshrc中失败了)。
这里的%表示值上的提示展开,%N表示“zsh当前正在执行的脚本、源文件或shell函数的名称”,无论哪个是最近启动的。如果没有,则相当于参数$0。(来自man zshmisc)

23
好的,这个翻译我可以担任。请看下面的内容:太好了 - 这应该适用于OP,但请注意,真正的 $BASH_SOURCE 等效项是 %x,而不是 %N,因为它在函数内部调用时仍引用封闭的 文件 - 换句话说:请使用 ${(%):-%x} - mklement0
1
嘿,阅读文档时,我不明白为什么我们需要在这里使用 :-,以及为什么 ${(%)%N} 不会起到同样的作用(注意:我知道它不起作用)。 - PierreBdR
除了 -%N 之外,还有哪些其他选项可用的文档? - Martin
1
注意:如果您的zshrc文件是符号链接,您可以使用readlink解析为绝对路径:readlink -f ${(%):-%N} - Bryce Guinta
PierreBdR: ${(%)%N} 类似于 ${%N},但受到了 (%) 标志的影响。${%N} 将尝试展开一个空字符串名称的变量(这不是真实存在的东西,但总是成功地展开为空),并删除一个尾随的 N。使用 ${(%):-%N} 尝试展开相同的空字符串变量,然后由于它是空的,使用 %N 作为默认值... 然后由于 (%) 标志的缘故,就像提示一样展开。 - Jonathan Klabunde Tomer
5
如果你想要更简洁的方式来查找包含源代码的目录的绝对路径,可以使用以下命令:${${(%):-%x}:A:h} - basicdays

67

${(%):-%x}zsh中最接近bash$BASH_SOURCE(以及ksh${.sh.file})的等价物,而不是$0

向Hui Zheng致敬,他在his answer中提供了关键指针和背景信息。

它返回封闭的脚本的(可能是相对的)路径,

  • 无论脚本是否被sourced
    • 具体来说,它也适用于初始化/配置文件,如~/.zshrc(与$0不同,在那里它莫名其妙地返回shell的路径)。
  • 无论是否从在脚本中定义的函数内部调用(与$0不同,在函数内部返回函数名称)。

我发现与$BASH_SOURCE唯一的区别在于以下晦涩的情况——这甚至可能是一个错误(在zsh 5.0.5中观察到):在一个被引用的脚本中的另一个函数内嵌函数中,当后来调用该嵌套函数时,${(%):-%x}不会返回包含脚本路径(返回空或“zsh”)。


${(%):-%x}的背景信息:

  • 在参数(变量)扩展(${...})中,(%):-代替变量名可以使用通常用于在提示符字符串中表示环境信息的转义序列,例如在PS1变量中用于确定作为主交互提示显示的字符串。

    • %是参数扩展标志的一个实例,所有这些标志都在man zshexpn下的Parameter Expansion Flags标题下列出。
  • %x是可用于提示字符串中的转义序列之一,其功能如上所述;还有许多其他转义序列,例如%d表示当前目录。

    • man zshmisc在标题SIMPLE PROMPT ESCAPES下列出了所有可用序列。

1
除了 -%x 之外,还有哪些其他选项可用的文档? - Martin

26
如果您想使脚本同时兼容Bash和Zsh,可以使用${BASH_SOURCE[0]:-${(%):-%x}}。当定义了BASH_SOURCE[0]时,结果值将从BASH_SOURCE[0]获取,否则将从${(%):-%x}}获取。请注意保留所有HTML标签。

11

$0是正确的。在源脚本中,它是脚本的名称,就像传递给.source内置一样(所以如果设置了path_dirs选项,则可能需要进行$path查找以找到脚本的实际位置)。

.zshrc未被源化,这就解释了为什么$0未设置为.zshrc。无论如何,您都知道文件名和位置:它是${ZDOTDIR-~}/.zshrc


我仍然不确定这是否正确。创建这两个文件:foo 包含 source ./foo2foo2 包含 echo $0。现在运行 ./foo,你将得到 ./foo 的输出,这不是被引用脚本的名称。将 $0 替换为 ${BASH_SOURCE[0]},使用 bash 重新运行,你将得到 ./foo2 - Thanatos
3
为了澄清并强调,对于zsh(以及可能的其他shell),$0的值是被引用的脚本而不是调用者的脚本。这在Bash中是不适用的。因此,在zsh中,$0与Bash中的${BASH_SOURCE[0]}相同。 - toxalot
@DavidFaure 你想以可移植的方式做什么?你是想找到主脚本的路径,还是源代码中当前代码所在的路径,或者其他什么? - Gilles 'SO- stop being evil'
1
@Gilles:.zshrc是被引用的 - 因为所有的初始化/配置文件都是按照它们的目的而被引用的。除此之外,zsh通常总是报告$0中的脚本路径,无论脚本是否被引用。 显然,唯一的例外是初始化/配置文件,在这种情况下,zsh的行为类似于bash和ksh,将$0设置为shell路径(由于引用)。 (因此,如果有什么不同,那么$0在这种情况下不反映脚本路径是另一个指示.zshrc正在被引用的指标。)真正的zsh等效于$BASH_SOURCE${(%):-%x} - mklement0
2
@toxalot:在zsh中,$0在大多数情况下等同于$BASH_SOURCE;但有两个例外:(a)在初始化/配置文件(例如〜/ .zshrc)中,$0不可思议地报告* shell *路径,以及(b)在函数内部它报告函数的名称。再次强调,真正的等效项是${(%): - %x} - mklement0
显示剩余3条评论

10

如果你正在将 .zshrc 符号链接到点文件目录中,并希望引用目录中的其他文件,请尝试以下方法:

SOURCE=${(%):-%N}
while [ -h "$SOURCE" ]; do
  DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
  SOURCE="$(readlink "$SOURCE")"
  [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE"
done
DOTFILES_DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"

(我从这里获取了循环脚本。)


1
除了 -%N 之外,还有哪些其他选项可用的文档? - Martin

2
也许你正在寻找 $_
# foo.sh
source foo2.sh

并且

# foo2.sh
echo $_

产量
# ./foo.sh
foo2.sh

1
为了获得最佳结果:${BASH_SOURCE:-$_} - mpapis
1
最佳实践是解释你提供的答案为什么有效,或者在像这样答案为<X特性>的情况下,包括该特性的基本解释。(甚至复制粘贴手册的简短部分也绝不是坏主意!) - ELLIOTTCABLE
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - David Faure
1
对于其他人想知道 $_ 是什么的:当你启动 shell 时(例如,$_ = /bin/bash),_ 变量被设置为你的 shell 的绝对文件名,或者如果在调用 shell 时传入了参数列表,则设置为正在执行的脚本。之后,它总是扩展为最后执行的命令或键入的参数的值。(来自 http://linuxshellaccount.blogspot.com/2008/04/shell-special-variables-in-bash.html) - ACK_stoverflow

1
将所有内容整合起来,以便在bash和zsh中都能正常工作:
SCRIPT_FILE_REL_PATH="${BASH_SOURCE[0]}"
if [[ "$SCRIPT_FILE_REL_PATH" == "" ]]; then
  SCRIPT_FILE_REL_PATH="${(%):-%N}"
fi
SCRIPT_FILE_PATH="`realpath $SCRIPT_FILE_REL_PATH`"
SCRIPT_DIR="`dirname $SCRIPT_FILE_PATH`"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接