我最近编写了一个名为 的函数zuperPrompt
,它打印出一个漂亮的提示,并且我在 .zshrc 中设置了 PROMPT 变量来调用该函数,如下所示:
setopt PROMPT_SUBST
PROMPT='$(zuperPrompt)'
发现同样的问题这里,但我认为问题出在 awk 命令中使用换行转义字符。我尝试在没有任何换行符的情况下完成整个提示,但得到了相同的结果:(
这是函数:
zuperPrompt() {
# count chars in directory name
num_chars=1
if [ ! $(dirs) = '~' ]; then
let "num_chars=$(basename "`pwd`" | wc -m) - 1"
fi
# draw line
line="\n╭──"
for i in {1..$num_chars}; do
line+="─"
done
line+="──╯"
directory="%F{8}%K{8}%B%F{blue}%1~%k%F{8}"
arrows="%B%F{magenta}❯%B%F{yellow}❯%F{cyan}❯ "
row1="\n%B%F{green}◆ "$directory"─╮"
row2=$line
row3="\n╰─ "$arrows
print -P $row1$row2$row3
}
这是输入后按 Tab 键的结果python3
我不确定这里发生了什么事。如果有人可以提供帮助,请提前致谢!
更新 在我的网络浏览器中查看此语法突出显示后,我发现 $row3 的颜色与其他颜色不同。我只用 $row1$row2 运行我的代码,它工作正常,没有光标移动。有人知道那里发生了什么事吗?
更新2 我从最终的打印语句中删除了 -P 选项,它适用于所有三行。
答案1
shell中的主要提示字符串是分配给或变量的zsh
字符串。它可能包含各种转义字符和代码,通知 shell 如何将字符串动态扩展为可见提示符。PROMPT
PS1
print -rP -- $PROMPT
您可以使用或自行扩展提示字符串print -rP -- $PS1
。
但是,如果您将一个字符串分配给PROMPT
已经用 扩展的字符串print -P
,则转义码告诉 shell 提示字符串的哪些部分是可见的以及提示字符串的哪些部分只是用于例如更改颜色的终端代码,shell 不能更长时间地跟踪提示的大小。这会引起您所描述的问题类型。另一个常见问题是长命令的换行变得混乱。
因此,不要使用print -P
来生成稍后分配给 的字符串PROMPT
,而是print -r
使用它来输出“原始”。
这与中描述的问题相同zsh 提示符未正确转义,但原因略有不同(您从提示字符串中删除有关非打印字符的有用信息,而不是从不添加有关非打印字符的信息)。
有点尴尬的代码
num_chars=1
if [ ! $(dirs) = '~' ]; then
let "num_chars=$(basename "`pwd`" | wc -m) - 1"
fi
可以写成单行
num_chars=$( print -n -P '%1~' | wc -m )
(%1~
是当前目录名称的提示代码,稍后您将使用该代码在提示中实际包含该字符串。)
还有循环
line="\n╭──"
for i in {1..$num_chars}; do
line+="─"
done
line+="──╯"
可以写成
printf -v line '\n╭──%*s──╯' $num_chars ''
line=${line// /─}
但你可以将这些组合成
print -v wd -n -P '%1~'
printf -v line '\n╭──%*s──╯' $#wd ''
line=${line// /─}
这摆脱了丑陋的命令替换和对wc
.
答案2
启用该promptsubst
选项后,您可以参数扩展,命令替换和算术展开在提示符扩展时被扩展,这样你就可以有PS1='$(command-that-generates-PS1-dynamically)'
,但扩展的结果仍然是一个提示字符串,其中%~
/%m
被扩展。
所以你最不想做的就是command-that-generates-PS1-dynamically
打电话print -P
。更应该是相反。如果command-that-generates-PS1-dynamically
输出要按字面显示,则应该确保%
(以及!
ifpromptbang
打开)被转义,并且所有不移动光标的控制序列都包含在%{
、%}
等中。
$PS1
除了所有转义符可以提供的文本之外,另一种更 zsh-y 的方法%x
是不设置动态文本$promptsubst
,而是在钩子中生成动态文本precmd
,并将它们存储在您在, ... 中$psvar
引用其成员的数组中。%1v
%2v
$PS1
所以,而不是:
command-that-generates-PS1-dynamically() {
local some_dynamic_data=$(some-cmd)
print -r -- "%F{red}${some_dynamic_data//\%/%%}%f$ "
}
set -o promptsubst
PS1='$(command-that-generates-PS1-dynamically)'
(只${some_dynamic_data//\%/%%}
负责转义 s %
,而不处理!
或转义可能包含的序列$some_dynamic_data
)。
做:
prompt-hook() {
psvar[1]=$(some-cmd)
}
precmd_function+=(prompt-hook)
PS1='%F{red}%1v%f$ '
这还节省了分叉子 shell 的麻烦(这也允许您prompt-hook
在提示扩展中维护一些数据),并且%1v
扩展(用于可打印文本)会照顾或转换不可打印的字符,使它们变得可见。
在你的情况下,这可能看起来像
display_width() REPLY=$(($#1 * 3 - ${#${(ml[$#1 * 2])1}}))
prompt-hook() {
display_width ${(%):-%1~}
psvar[1]=${(l[$REPLY][─])}
}
precmd_functions+=(prompt-hook)
() {
local directory='%F{8}%K{8}%F{blue}%1~%k%F{8}'
local arrows='%B%F{magenta}❯%F{yellow}❯%F{cyan}❯'
local line=$'─╮\n╭──%1v──╯\n╰─'
PS1=$'\n'"%B%F{green}◆ ${directory}${line} ${arrows}%b%f "
}
其中$PS1
是 static (不需要$promptsubst
;我们只是在此处使用临时临时变量来提高易读性),但动态自定义部分(与 展开的显示宽度长度相同的行部分%1~
)生成为我们钩子的一部分precmd
。
您会注意到,在此过程中没有分叉任何子 shell。
看这个答案到获取字符串的显示宽度有关计算字符串显示宽度的函数的详细信息display_width
(不一定与其中的字符数相同)。