![我如何才能正确地制作带有错误颜色和表情符号的 bash 提示?](https://linux22.com/image/1200391/%E6%88%91%E5%A6%82%E4%BD%95%E6%89%8D%E8%83%BD%E6%AD%A3%E7%A1%AE%E5%9C%B0%E5%88%B6%E4%BD%9C%E5%B8%A6%E6%9C%89%E9%94%99%E8%AF%AF%E9%A2%9C%E8%89%B2%E5%92%8C%E8%A1%A8%E6%83%85%E7%AC%A6%E5%8F%B7%E7%9A%84%20bash%20%E6%8F%90%E7%A4%BA%EF%BC%9F.png)
解决第一个问题后问题使用我的自定义提示,我还剩下一个。
当我通过上下箭头键循环浏览我最近使用的命令时,有时前一个命令中的一些字符仍然可见,尽管它们无法访问,也不在命令行中。它们只是一个视觉错误,非常烦人且令人困惑。
在这里,我在命令历史记录中向上移动了一点,然后返回到当前(空)提示符并输入echo
。我的光标无法访问pip i
(来自上一个命令)。它在那里可见但实际上并不存在。pip install
我.bashrc
有此用于自定义提示的代码:
set_PS1()
{
local Reset="\\[$(tput sgr0 )\\]"
local Bold="\\[$(tput bold )\\]"
local Red="\\[$(tput setaf 1 )\\]"
local Green="\\[$(tput setaf 2 )\\]"
local Yellow="\\[$(tput setaf 3 )\\]"
local Blue="\\[$(tput setaf 4 )\\]"
local MagentaBG="\\[$(tput setab 5 )\\]"
local Cyan="\\[$(tput setaf 6 )\\]"
local Whoami='\u'
local Where='\w'
local Hostname='\h'
local Time='\D{%H:%M:%S}'
local Exit_Code="$?"
exit_code_prompt() {
local Exit_Code="$?"
local Red="$(tput setaf 1 )"
local Green="$(tput setaf 2 )"
if (($Exit_Code == 0 )); then
printf '%s\xE2\x9C\x93 \xE2\x86\x92 ' "$Green" # Green checkmark symbol
else
printf '%s\xE2\x9C\x98 %s \xE2\x86\x92 ' "$Red" "$Exit_Code" # Red cross mark symbol and exit code
fi
}
local Line_1="$Bold$Yellow$Time $Cyan$Whoami$Blue@$Cyan$Hostname$Reset$Bold":" $Blue$Where$Reset"
local Line_2="$Bold\$(exit_code_prompt)$Reset$Bold \$: $Reset"
#local Line_2="$Bold \$: $Reset"
PS1="$Line_1\n$Line_2"
unset -f set_PS1
}
set_PS1
我已经将问题缩小到exit_code_prompt
函数上,因为如果我从中删除它,问题就不会出现$Line_2
编辑: 当我将函数内部的颜色定义放入括号中时,就像在外面一样:
exit_code_prompt() {
local Exit_Code="$?"
local Red="\\[$(tput setaf 1 )\\]"
local Green="\\[$(tput setaf 2 )\\]"
if (($Exit_Code == 0 )); then
printf '%s\xE2\x9C\x93 \xE2\x86\x92 ' "$Green" # Green checkmark symbol
else
printf '%s\xE2\x9C\x98 %s \xE2\x86\x92 ' "$Red" "$Exit_Code" # Red cross mark symbol and exit code
fi
}
我得到这个结果:
答案1
新的解决方案
PS1
事实证明,使用 just和 not确实可以做到这一点PROMPT_COMMAND
。我们不需要设置任何其他全局变量。以下是一行代码:
PS1=$'\e[1;33m\\t \e[36m\u\e[34m@\e[36m\h\e[;1m: \e[34m\w\n \[\b\e[;1;31m✘\] ${?/#0/\[\b\b\e[32m✓ \]}${?/#[1-9]*/ }\[\b→\e[;1m\] \$: \[\e[m\]'
我原来的解决方案的改进如下:
- 只有最后一行需要
\[\]
- 我将 扩展为
tput
。\e[...m
这在 Ubuntu 上是兼容的,不用担心,因为默认颜色.bashrc
也会这样做 - 连续
\e[Xm\e[Ym
折叠成\e[X;Ym
,并且前一个粗体并不总是需要重置 \D{%H:%M:%S}
=\\t
- 未知
\X
序列不需要反斜杠转义 - 我依靠的
$?
是0
或正数而没有前导零:?/#0*/
=?/#0/
${?/#[1-9]*/ }\[\b
插入一个空格,然后插入非零字符(当前为空格)的任意字符,以及零字符(当前为 0)的任意字符。然后删除额外的字符,以实现仅对非零字符插入一个空格。[1-9]*
是一个表示 1-9 之间的任意数字的 glob,而不是表示前面任意数字的正则表达式✘\] ${?/#0/\[\b\b\e[32m✓ \]}
总是先打印红色十字标记符号。如果为零,我们返回并将其替换为绿色复选标记。如果非零,我们则打印错误代码
指导解决方案
这是一个工作提示:
set_PS1() {
local Exit_Code="$?"
local Reset="\\[$(tput sgr0 )\\]"
local Bold="\\[$(tput bold )\\]"
local Red="\\[$(tput setaf 1 )\\]"
local Green="\\[$(tput setaf 2 )\\]"
local Yellow="\\[$(tput setaf 3 )\\]"
local Blue="\\[$(tput setaf 4 )\\]"
local MagentaBG="\\[$(tput setab 5 )\\]"
local Cyan="\\[$(tput setaf 6 )\\]"
local Whoami='\u'
local Where='\w'
local Hostname='\h'
local Time='\D{%H:%M:%S}'
# Emoji are multi-byte, but shells think 1 byte = 1 character
# We add a 1-character space, then enable color-code mode to ignore
# the upcoming emoji, then backspace to draw the emoji on top of the space.
local emoji_start=$' \\[\b'
local emoji_end=$'\\]'
if (($Exit_Code == 0 )); then
local exit_code_prompt="$Green$emoji_start"$'\xE2\x9C\x93'"$emoji_end" # Green checkmark symbol
else
local exit_code_prompt="$Red$emoji_start"$'\xE2\x9C\x98'"$emoji_end $Exit_Code" # Red cross mark symbol and exit code
fi
local Line_1="$Bold$Yellow$Time $Cyan$Whoami$Blue@$Cyan$Hostname$Reset$Bold":" $Blue$Where$Reset"
local Line_2="$Bold$exit_code_prompt $emoji_start"$'\xE2\x86\x92'"$emoji_end$Reset$Bold \\$: $Reset"
#local Line_2="$Bold \$: $Reset"
PS1="$Line_1\\n$Line_2"
}
# We need to expand exit_code_prompt BEFORE PS1 prompt string expansion (see shopt promptvars in man bash)
# so we want to move it to PROMPT_COMMAND which runs earlier.
PROMPT_COMMAND=set_PS1
PS1
经历迅速扩张然后替代,所以我们需要PROMPT_COMMAND
- 这会泄露
set_PS1
全局变量,但你已经exit_code_prompt
泄露了全局变量 "\$"
需要及时"\\$"
评估,而不是立即- 表情符号是多字节的,也需要换行,只不过这次我们需要插入一个假空格,因为它们的宽度是 1 个字符,而不像颜色代码的宽度是 0 个字符
printf
没有必要那么复杂,因为你可以直接这样做$''
现在↑/命令历史记录可以正常工作。使用+ /↓滚动多行提示现在也会跳转到正确的位置。Ctrl←→
这里有些例子: