Bash PS1:来自外部命令的非打印字符的换行问题

时间:2014-07-19 10:20:27

标签: linux bash escaping command-prompt prompt

我正在使用外部命令填充我的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=...命令变得复杂。 但是,颜色的转义码必须来自外部命令。

提前致谢!

3 个答案:

答案 0 :(得分:19)

我无法准确地告诉您为什么这有效,但将\[\]替换为bash在提示符中生成的实际字符:

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

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

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

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

PROMPT_COMMAND=prompt_cmd

这样,\[\]定义时会添加到PS1,而不会在评估时添加到\001 ,因此您无需直接使用\002和{{1}}。

答案 1 :(得分:0)

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

以下内容将ANSI控制序列括在ASCII SOH^A)和STX^Bwhich are equivalent to \[ and \] respectively中:

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"

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

答案 2 :(得分:-1)

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

发生用于颜色规范,但正如您所发现的,它将“非打印序列”标记变成$PS1解释器不正确的东西理解。

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