【问题标题】:Bash PS1: line wrap issue with non-printing characters from an external commandBash PS1:来自外部命令的非打印字符的换行问题
【发布时间】:2014-09-10 10:24:50
【问题描述】:

我正在使用一个外部命令来填充我的 bash 提示符,该提示符在每次评估 PS1 时运行。但是,当此命令输出不可打印的字符(如颜色转义码)时,我遇到了问题。 这是一个例子:

$ cat green_cheese.sh 
#!/bin/bash
echo -e "\033[32mcheese\033[0m"

$ export PS1="\$(./green_cheese.sh) \$"
cheese $ # <- cheese is green!
cheese $ <now type really long command>

在 PS1 提示符中处理非打印字符的规范方法是将它们包含在 \[\] 转义序列中。问题是,如果您从外部命令执行此操作,则 PS1 解释器不会解析这些转义:

$ cat green_cheese.sh 
#!/bin/bash
echo -e "\[\033[32m\]cheese\[\033[0m\]"
$ export PS1="\$(./green_cheese.sh) \$"
\[\]cheese\[\] $ # <- FAIL!

我可以从外部命令中使用特定的转义序列来获得所需的结果吗?或者有没有办法手动告诉提示设置提示宽度的字符数?

假设我可以从外部命令打印任何我喜欢的东西,并且这个命令可以非常智能(例如,计算输出中的字符)。我还可以根据需要使export PS1=... 命令变得复杂。 但是,颜色的转义码必须来自外部命令。

提前致谢!

【问题讨论】:

    标签: linux bash escaping command-prompt prompt


    【解决方案1】:

    我无法确切地告诉你为什么这行得通,但是将\[\] 替换为bash 在提示中从它们生成的实际字符:

    echo -e "\001\033[32m\002cheese\001\033[0m\002"
    

    [我从一些我现在找不到的 Stack Overflow 帖子中了解到这一点。]

    如果我不得不猜测,那就是 bash 在执行嵌入在提示符中的命令之前将 \[\] 替换为两个 ASCII 字符,所以当 green_cheese.sh 完成时,为时已晚让bash 正确处理包装器,因此它们被逐字处理。避免这种情况的一种方法是使用PROMPT_COMMAND 动态构建提示,而不是在PS1 的值中嵌入可执行代码。

    prompt_cmd () {
        PS1="$(green_cheese.sh)"
        PS1+=' \$ '
    }
    
    PROMPT_COMMAND=prompt_cmd
    

    这样,\[\] 在被定义时添加到PS1,而不是在评估时添加,所以你不需要需要直接使用\001\002

    【讨论】:

    • 谢谢。这完美!实际上,现在我知道要使用的转义码了,我找到了您参考的其他帖子:stackoverflow.com/a/22706988/341459stackoverflow.com/a/14637194/341459stackoverflow.com/a/19501528/341459
    • 你完全正确。 \001\002,又名 RL_PROMPT_START_IGNORE 和 RL_PROMPT_END_IGNORE,是一个记录不充分的 readline feature。 Bash 将 Readline 的 \[..\] 转换为 \001..\002,但忽略了转义现有的,因此这个实现细节会泄漏(它非常有用,你几乎不能称之为错误)。
    • 当我在最近的一篇文章中看到PROMPT_COMMAND 时,我想起了这个问题并回到了它,但似乎这个想法已经给出。我还认为 bash 实际上可能会从 \[\] 中生成字符,并尝试对其进行检查,但 ttyrec 的输出令人困惑,所以我放弃了。 +1 顺便说一句。
    【解决方案2】:

    我怀疑如果您在第一个示例之后回显$PS1 的值,您会发现它的值是绿色的“奶酪”一词。 (至少,这是我在运行您的示例时看到的。)乍一看,这就是您想要的——绿色的“奶酪”一词!除了您真正想要的是前面带有产生绿色的转义码的单词cheese。您通过使用 -e 标志来回显所做的是生成一个带有转义码的值已经评估

    碰巧适用于颜色规范,但正如您所发现的,它会将“非打印序列”标记破坏为$PS1 解释器无法正确理解的内容。

    幸运的是,解决方案很简单:删除 -e 标志。然后echo 将保持转义序列不变,$PS1 解释器将做正确的事情™。

    【讨论】:

    • 听起来很公平,但不起作用。 chepner 已经描述了在调用外部命令之前如何进行转义。
    【解决方案3】:

    如果您无法编辑生成包含 ANSI 颜色/控制代码的字符串的代码,您可以在事后将它们包装起来。

    下面将用 ASCII SOH (^A) 和 STX (^B) which are equivalent to \[ and \] respectively 括起 ANSI 控制序列:

    function readline_ANSI_escape() {
      if [[ $# -ge 1 ]]; then
        echo "$*"
      else
        cat  # Read string from STDIN
      fi | \
      perl -pe 's/(?:(?<!\x1)|(?<!\\\[))(\x1b\[[0-9;]*[mG])(?!\x2|\\\])/\x1\1\x2/g'
    }
    

    像这样使用它:

    $ echo $'\e[0;1;31mRED' | readline_ANSI_escape
    

    或者:

    $ readline_ANSI_escape "$string"
    

    作为奖励,多次运行该函数不会重新转义已经转义的控制代码。

    【讨论】:

      猜你喜欢
      • 2020-05-16
      • 1970-01-01
      • 1970-01-01
      • 2018-09-09
      • 1970-01-01
      • 2020-10-09
      • 2019-10-03
      • 2012-01-18
      相关资源
      最近更新 更多