将 shell 配置为始终在新行上打印提示,例如 zsh



如果命令的输出不以n结尾,则下一个提示符会尴尬地紧随其后出现:

$ echo -n hai
hai$

我刚刚注意到一位同事的外壳(zsh,它的价值)配置为打印%(背景和前景色反转以强调),在这种情况下后跟一个n

$ echo -n hai
hai%
$

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


更新

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

  • ESC[6n 是用于访问光标位置 (http://en.wikipedia.org/wiki/ANSI_escape_code) 的控制序列介绍器。

  • 在 OS X 上使用 echo 时,e 不是 ESC 的有效表示形式 (https://superuser.com/q/33914/176942)。 可以改用33

  • IFS 是 Bash 的内部字段分隔器 (http://tldp.org/LDP/abs/html/internalvariables.html#IFSREF)。

  • read -sdR看起来像read -s -d -R的简写,但实际上"R"不是一个标志,它是-d(分隔符)选项的值。为了避免混淆,我决定写read -s -d R

  • 双括号构造,(( ... )),允许算术扩展和计算(http://tldp.org/LDP/abs/html/dblparens.html)。

这是我的 .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 '33[6n'
  IFS=';' read -s -d R -a curpos
  curpos[0]="${curpos[0]:2}"  # strip leading ESC[
  (( curpos[1] > 1 )) && echo -e '33[7m%33[0m'
  # set PS1...
}
export PROMPT_COMMAND=set_prompt

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

使用PROMPT_COMMAND的小技巧:

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

因此,如果您将其放在您的.bashrc中:

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

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

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

优点:

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

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


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

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

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

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

  2. 显式清除 tty 缓冲区:

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

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

作为旁注:与其在数组curposread,不如直接获取光标在变量中的位置,例如curxcury,如下所示:

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

如果您只需要 y 位置cury

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

感谢 unix.stackexchange 上的 Gilles:

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

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

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

解释:

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

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

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

现在在它前面加上如下n

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

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

最新更新