17

如果命令的输出不以 结尾\n,那么接下来会尴尬地立即出现下一个提示:

$ echo -n hai
hai$

我刚刚注意到一位同事的外壳(zsh,值得一提)被配置为在这种情况下打印 a %(背景和前景色倒置以强调),然后是 a :\n

$ echo -n hai
hai%
$

我也想这样做。我使用 Bash。这可能吗?如果是这样,我会在我的 ~/.bashrc 中添加什么?


更新

我花了几个小时来了解 gniourf_gniourf 的解决方案是如何工作的。我将在这里分享我的发现,以防它们对其他人有用。

这是我的.bashrc中的相关片段:

set_prompt() {
  # CSI 6n reports the cursor position as ESC[n;mR, where n is the row
  # and m is the column. Issue this control sequence and silently read
  # the resulting report until reaching the "R". By setting IFS to ";"
  # in conjunction with read's -a flag, fields are placed in an array.
  local curpos
  echo -en '\033[6n'
  IFS=';' read -s -d R -a curpos
  curpos[0]="${curpos[0]:2}"  # strip leading ESC[
  (( curpos[1] > 1 )) && echo -e '\033[7m%\033[0m'

  # set PS1...
}

export PROMPT_COMMAND=set_prompt

注意:该curpos[0]="${curpos[0]:2}"行是不必要的。我将其包含在内,因此可以在行也相关的上下文中使用此代码。

4

3 回答 3

15

使用一个小技巧PROMPT_COMMAND

PROMPT_COMMAND在 Bash 打印每个主要提示之前检查变量的值。如果PROMPT_COMMAND已设置且具有非空值,则执行该值就像在命令行上键入它一样。

因此,如果你把它放在你的.bashrc

_my_prompt_command() {
    local curpos
    echo -en "\E[6n"
    IFS=";" read -sdR -a curpos
    ((curpos[1]!=1)) && echo -e '\E[1m\E[41m\E[33m%\E[0m'
}
PROMPT_COMMAND=_my_prompt_command

你会很好的。随意在echo "%"零件中使用其他花哨的颜色。您甚至可以将其内容放在一个变量中,以便您可以即时修改它。

诀窍:在打印提示之前获取光标所在的列(echo -en "\E[6n"后跟read命令),如果它不是 1,则打印 a%和换行符。

优点:

  • 纯 bash(无外部命令),
  • 没有子壳,
  • 让你的PS1一切变得干净整洁:如果你PS1有时想改变你的(我在深度嵌套的目录中工作时会这样做——我不喜欢在几英里内运行提示),这仍然有效。

作为三重注释,您可以使用stty而不是回显硬编码的控制序列。但这使用外部命令,不再是纯 bash。适应您的需求。


关于随机打印的丑陋字符代码的问题:这可能是因为 tty 缓冲区中还有一些东西。可能有几个修复:

  1. 关闭然后再打开echo终端,使用stty.

    set_prompt() {
        local curpos
        stty -echo
        echo -en '\033[6n'
        IFS=';' read -d R -a curpos
        stty echo
        (( curpos[1] > 1 )) && echo -e '\033[7m%\033[0m'
    }
    PROMPT_COMMAND=set_prompt
    

    主要区别在于echo/read组合已用stty -echo/包装stty echo,分别禁用和启用终端上的回显(这就是为什么该-s选项read现在无用的原因)。在这种情况下,您将无法正确获取光标位置,这可能会导致奇怪的错误消息,或者%根本不输出。

  2. 显式清除 tty 缓冲区:

    set_prompt() {
        local curpos
        while read -t 0; do :; done
        echo -en '\033[6n'
        IFS=';' read -s -d R -a curpos
        (( curpos[1] > 1 )) && echo -e '\033[7m%\033[0m'
    }
    PROMPT_COMMAND=set_prompt
    
  3. 如果无法清理 tty 缓冲区就放弃:

    set_prompt() {
        local curpos
        if ! read -t 0; then
            echo -en '\033[6n'
            IFS=';' read -s -d R -a curpos
            (( curpos[1] > 1 )) && echo -e '\033[7m%\033[0m'
        # else
        #     here there was still stuff in the tty buffer, so I couldn't query the cursor position
        fi
    }
    PROMPT_COMMAND=set_prompt
    

作为旁注:您可以直接在变量中获取光标的位置,而不是read在数组中,例如,这样:curposcurxcury

IFS='[;' read -d R _ curx cury

如果您只需要 y 位置cury

IFS='[;' read -d R _ _ cury
于 2013-11-22T23:42:22.863 回答
4

感谢unix.stackexchange上的 Gilles :

如果上一个命令将光标留在最后一个边距以外的某个位置,您可以让 bash 在下一行显示其提示。把它放在你的 .bashrc 中(GetFreeDennis Williamson提案的变体)

从两个链接的答案中,我提炼了这个解决方案:

PS1='\[\e[7m%\e[m\]$(printf "%$((COLUMNS-1))s")\r$ '

解释:

  • \[\e[7m%\e[m\]-- 反向视频百分号
  • printf "%$((COLUMNS-1))s"——COLUMNS-1空格。如果设置了选项,该COLUMNS变量将存储终端的宽度。checkwinsize由于printf它位于$()子外壳中,因此它的输出不会打印到屏幕上,而是添加到PS1
  • \r回车符

所以,基本上,它是一个%符号,一长串空格,后跟一个返回键。这行得通,但老实说,我不明白为什么这会产生预期的效果。具体来说,为什么它看起来只在需要时才添加换行符,否则没有额外的换行符?为什么那里有必要的空间?

于 2013-11-22T19:10:42.503 回答
0

如果你这样做echo $PS1,你会看到你的提示的当前代码,如下所示:
\[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\u@\h:\w\$

现在在它前面加上\n这样的:

PS1="\n\[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\u@\h:\w\$"

现在您的提示将始终从新行开始。

于 2013-11-13T03:37:34.160 回答