ZSH提示符替换问题

3
我在这里和谷歌上搜索了几个答案,但我仍然不确定我的提示出了什么问题。
根据我阅读的文档,这应该是有效的。
setopt prompt_subst
autoload -U colors && colors
PROMPT="%{[00m[38;5;245m%}test %D%{[00m%}"

不过,我需要翻译的内容是:

[00m[38;5;245mtest 15-07-01[00m

请注意日期扩展实际上可行,因此提示符替换正在工作。ZSH的提示符扩展手册说明%{...%}应该被视为原始转义码,但似乎并没有发生这种情况。将该字符串传递给print -P也会产生上面的输出。我在互联网上找到了ZSH的示例提示符,也似乎表明上述语法应该起作用。请参阅此处,其中$FG和$FX数组填充了转义码,并在此处定义。我直接尝试了这个示例,将上述两个文件合并,添加setopt prompt_subst以确保它被设置,然后进行源码编译,结果提示符成为了一堆转义代码。
下面的内容是有效的。
setopt prompt_subst
autoload -U colors && colors
PROMPT=$'%{\e[00m\e[38;5;245m%}test %D%{\e[00m%}'

我在不同的操作系统上(OSX Yosimite中的ZSH 5.0.5,MacPorts中的5.0.7以及Debian的4.3.17)测试了这个代码段,并得到了预期结果 test 15-07-01 以正确的颜色显示。虽然我已经提供了一个有效的解决方案来解决我的问题,但是我仍然对为什么第一种语法没有按照预期工作感到好奇。请注意,保留了HTML标签。

这里没有问题;如果你正在阅读的文档省略了转义字符,那么它是错误的。指示终端切换颜色的 ANSI 代码都以 ESC[ 开头,而不仅仅是 [ - chepner
1
请注意,使用内置的转义字符来更改颜色要比处理原始控制序列容易得多。prompt="%F{245}test %D%f" - chepner
1
你可能认为 %{ ... %} 提供了每个序列开头的转义字符;实际上,它只是指示 shell,花括号的内容在输出中占用零空间,以便可以计算出提示符的正确屏幕长度。 - chepner
文档措辞不太好。转义序列不包含可打印字符;整个内容由终端解释而非显示。如果您在大括号内放置可打印字符,则您的提示符实际上比 zsh 计算出的要更长,相对于如果您根本没有使用 %{...%},则提示符会比 zsh 计算出的要短。 - chepner
我知道花括号告诉zsh它们之间的内容应该被视为零长度。我也很了解ANSI转义码。只是我对ZSH不太熟悉。我发现很难看出我链接的配置文件应该如何工作。spectrum文件甚至在oh-my-zsh中使用(我故意不将其用作其他示例 - 我喜欢知道我的配置文件是如何编写和执行的)。 - DuckPuppy
显示剩余2条评论
1个回答

0

我认为这一切都与逃脱的永恒问题有关。值得提醒自己的是,逃脱意味着什么,简要地说:转义字符是向计算机指示接下来的内容不应直接输出。

所以这里有两个转义问题: PROMPT="%{[00m[38;5;245m%}test %D%{[00m%}"

首先,颜色转义序列(例如;[00m)应该全部以控制字符开头,像这样:\e[00m。你可能还见过它被写成 ^[00m\003[00m。我怀疑发生了其中一个变体被无意中从作者的复制/粘贴或网站框架栈中逃脱的常见命运,无论是在数据库、HTTP呈现还是JS解析的某个地方。控制字符(即^\e或者\003),就像你可能知道的那样,没有文字表示,例如如果你在键盘上按下它。这就是为什么Web堆栈可能决定在字符串中看到它时不显示任何东西。所以,让我们现在来纠正这个问题:

PROMPT="%{\e[00m\e[38;5;245m%}test %D%{\e[00m%}"

这实际上很好地过渡到了下一个转义问题。有点滑稽的是,\e[ 实际上是 ESC 的一种表示形式,因此它本身就是一个转义序列标记,而且也被 \ 转义了。这是一种关于旧的 \\\\\\\\\\ 笑话的变化。现在,我们必须清楚地区分终端的转义表达式和提示字符串的替换,在伪代码中:

PROMPT="%{terminal colour stuff%}test %D%{terminal colour stuff%}"

现在我怀疑发生的是,虽然我找不到任何文档来证明它,但一旦ZSH完成了它的替换,或者在替换过程中,所有的文字字符,无论转义标志如何,都会被提升为真实字符¹。更有趣的是,这个提升可能是通过转义所有的转义字符来完成的。例如,如果你想在命令行上打印 '\e',你必须这样做:echo "\\\e"。因此,为了克服这个问题,我们只需要确保“终端颜色内容”转义序列在被分配给PROMPT之前就被评估了,这可以简单地用$''模式来实现,像这样:

PROMPT=$'%{\e[00m\e[38;5;245m%}test %D%{\e[00m%}'

请注意,$''$()${} 是同一类,但它的唯一功能是解释转义序列。

[1] 我怀疑的原因是你实际上可以做以下操作:

PROMPT='$(date)'

其中$(date)%D具有相同的作用,通过在每次新提示输出到屏幕时打印日期的实时版本。这个特定的例子所展示的是,PROMPT变量应该被视为一个小脚本的存储,而不是字符串(尽管这两个概念之间存在重叠,因此会产生混淆)。因此,作为一个脚本,该字符串首先被评估,然后再被打印出来。我没有查看过ZSH的提示渲染代码,但我认为这样的评估将受益于本地使用转义序列。例如,如果您想将转义序列作为参数传递给提示中运行的命令(每次提示呈现都会运行的命令),该怎么办?例如,以下内容在功能上与上面讨论的提示相同:

PROMPT='%{$(print "\e[00m\e[38;5;245m")%}test $(date)%{$(print "\e[00m")%}'

转义序列被字面存储,并且只在每个提示符呈现时解释。


\e 的正确八进制表示是 \033,而不是 \003。 - Slaven Rezic

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