最近我想知道在 Bash 中获取当前正在运行的脚本/文件的路径的最佳方法。
我找到了很多答案。特别是其中之一是:
"${BASH_SOURCE:-${(%):-%x}}"
Run Code Online (Sandbox Code Playgroud)
我记得当时决定这是满足我需求的最佳解决方案。现在我正在努力记住为什么。我知道一些片段的意思,但不是全部。即使有了我所知道的,我也不能把它们放在一起来理解它。有用。但为什么?
我为在网络上的任何地方找到这个特定代码所做的一切努力(包括搜索它的一部分)都失败了,所以我无法找到我最初找到它的地方。
谁能告诉我每件作品的含义(甚至可以说明为什么我可能会选择这个而不是其他答案)?
谢谢!
第二天更新:@rici 的精彩回答(和评论)解释了一切,包括我选择它的原因。
似乎我选择了整个表达式来使我正在使用它的源文件能够在 bash 和 zsh 中可靠地使用 - 这是一个目标。使用双重替换,整个表达式在任一 shell 中得到相同的答案。
解释了为什么我无法理解(或通过谷歌找到任何合理的)bash 中的 ${(%):-%x} 部分,因为......好吧......它在 bash 中没有多大意义,哈哈,因为它是为 zsh 设计的。出于这个原因,我现在为此添加了一个 zsh 标签。
一个小时后的第二次更新,以防这对任何人有帮助:我现在已经找到了:
...这是我${(%):-%x}最初从哪里得到的,最值得注意的是这个特定的答案:
这个特殊的表达式试图在 bash 和 zsh 中提供相同的结果:当前运行脚本的文件路径。
在 bash 中,
"${BASH_SOURCE:-${(%):-%x}}"
Run Code Online (Sandbox Code Playgroud)
意思是“$BASH_SOURCE如果它存在并且不为空,否则${(%):-%x}”。只要您在$BASH_SOURCE定义为非空值的上下文中使用它,否则${(%):-%x}将永远不会使用无效的替换并且 bash 不会抱怨它。[注1]
有关详细说明,$BASH_SOURCE请参阅@mklement0 的这个优秀答案。
现在,在 zsh 中,$BASH_SOURCE通常没有定义变量(尽管我认为它可能是从父级导出的),因此确实会发生替换,然后 zsh 将其替换为 expand ${(%):-%x}。Bash 用户可能会发现它更神秘,但我们可以将其分解如下:
${...:-...} 与 bash 中的意思相同:如果左手部分为空或未定义,则使用右手部分。与 bash 不同,zsh 允许左侧部分完全为空,而不仅仅是一个值为空的变量。所以,{:-foo}是写作的一个复杂的方式foo。
${(flags)...}导致在扩展上评估指定的标志。在这种情况下,我们有 flag %,这意味着应该对参数扩展进行“提示扩展”。[笔记2]
没有处理标志,我们只剩下{:-%x}; 如第 1 点所示,这等效于 string %x。但是该(%)标志会触发%x. 而在 zsh 提示扩展中,%x是——等待它——当前正在执行的脚本文件的名称。
简而言之,在 zsh 中,${(%):-%x}与$BASH_SOURCEbash 中的含义(几乎)完全相同。因此,${BASH_SOURCE:-${(%):-%x}}扩展到 bash 和 zsh 中的当前脚本源文件。
$BASH_SOURCE另一方面,如果未定义或为空,则会产生“错误替换”。正如我们稍后将看到的,${(flags)...}是 zsh 特定的语法,但这不是 bash 抱怨它的原因。对于 bash,它看起来像是%对不存在的参数的后缀删除(运算符)$(。它抱怨因为$(不是有效的参数名称。
为了进行比较,请考虑非常相似但有效的替换${#%):-%x}。它与 具有相同的值$#,因为$#不以 结尾):-%x。这种解析对人的眼睛来说可能会令人惊讶,但 bash 却把它看得一清二楚。
Zsh 与 bash 一样,具有高度可定制的提示,在提示变量之一中使用特殊的转义序列。(Posix 标准提示变量是$PS1,$PS2和$PS4;bash 和 zsh 都提供了其他几个变量。)Bash 提供了一些特殊的反斜杠序列(\u例如,是当前用户名),并且还允许在扩展提示时解释任意参数扩展。Zsh 也有多种转义序列,但%用作转义字符(%n是当前用户名),如果prompt_subst设置了shell 选项,则允许参数扩展。
请注意,您通常无法像在 bash 中一样zsh通过简单的设置来试验提示$PS1,因为大多数zsh安装使用“提示主题”。启用提示主题后,您需要创建自己的主题以更改提示字符串,因为在每个命令之前都会自动应用主题设置。