我如何才能正确地制作带有错误颜色和表情符号的 bash 提示?

我如何才能正确地制作带有错误颜色和表情符号的 bash 提示?

解决第一个问题后问题使用我的自定义提示,我还剩下一个。

当我通过上下箭头键循环浏览我最近使用的命令时,有时前一个命令中的一些字符仍然可见,尽管它们无法访问,也不在命令行中。它们只是一个视觉错误,非常烦人且令人困惑。

看起来像这样:
在此处输入图片描述

在这里,我在命令历史记录中向上移动了一点,然后返回到当前(空)提示符并输入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

这里有些例子:

  • 提示演示:

    提示演示

  • 长字符串:

    长字符串

  • 长字符串然后Ctrl+

    长字符串然后 ←

  • pip install然后echo

    pip 安装然后 echo

  • echo然后在历史上:

    echo 然后 ↑ 在历史记录中

相关内容