【问题标题】:Configure shell to always print prompt on new line, like zsh将 shell 配置为始终在新行上打印提示,例如 zsh
【发布时间】:2013-11-25 10:42:46
【问题描述】:

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

$ echo -n hai
hai$

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

$ echo -n hai
hai%
$

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


更新

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

这是来自我的.bashrc 的相关sn-p:

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}" 行是不必要的。我将其包含在内,因此可以在行也相关的上下文中使用此代码。

【问题讨论】:

  • 何不问问他是怎么做到的。您的~/.bashrc 中的sn-p 很可能与他的~/.zshrc 相同。这个问题可能更适合Server Fault,但是您只想追加到没有\n 中断的行?
  • 我确实问过他,我们查看了他的 ~/.zshrc 但找不到负责的代码。是的,我只想附加到不以 \n 结尾的输出。
  • 这是我昨晚在 Google 上找不到的东西,而且似乎是 zch 特有的,因为它确实有很多新的线路处理信息。我今晚晚些时候会调查这个,因为我现在想知道如何自己做。
  • "我要在 ~/.bashrc 中添加什么?" exec zsh.
  • zsh 在内部使用$COLUMN - 1 spaces then \r trick,如果有人仍然好奇为什么他们在朋友的.zshrc 中找不到它。

标签: bash shell prompt


【解决方案1】:

如果您执行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\$"

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

【讨论】:

  • 这会在常见情况下添加一个额外的\n(当命令的输出以\n 结尾时)。
  • @davidchambers 是的:-)
  • 是的,我首先想到的是 PS1,但 OP 只想在前一个输出没有 /n 返回时追加。
  • 你根本没有解决这个问题。当输出没有换行符时,OP only 要求换行符,并且他还要求使用倒置的% 指示。顺便说一句,我刚刚安装了zsh,默认情况下它会这样做,所以它很可能很神奇......
  • @janos:别那么苛刻,我让他半途而废,展示了如何在提示符中添加类似 \n 的内容。如果你知道得更好,请给我看代码。
【解决方案2】:

感谢 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 位于$() 子shell 中,因此其输出将添加到PS1,而不是打印到屏幕上
  • \r一个回车符

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

【讨论】:

  • "这并不能完全解决您的问题,因为我不知道如何像 zsh 那样将 % 放在行尾。" -- 丹尼斯威廉姆森的回答(你链接到的)就是这样做的。
  • 它的工作原理是根本不输出换行符 - 它在屏幕的右边缘留出空间,然后输出回车符 - 而不是换行符 - 将光标移动到当前行的开头。如果前一行在行首以外的任何地方结束,则空格将换行,CR 将直接转到该短行的开头。
  • @evilotto 空格数呢?为什么精确地拥有COLUMNS-1 空格很重要?
【解决方案3】:

使用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,则打印 % 和换行符。

优点:

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

作为三元组 cmets,您可以使用 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
    

附带说明:您可以直接在变量中获取光标的位置,而不是在数组curpos 中使用read,例如curxcury,如下所示:

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

如果只需要y位置cury

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

【讨论】:

  • 令人印象深刻!这是一个漂亮的解决方案。最后一个问题:为什么要引用函数名,为什么不简单PROMPT_COMMAND=_my_prompt_command
  • @janos 感谢您的评论。你说得对,那里的引号没用,我会删除它们。
  • 我想你可以通过使用stty而不是echo硬编码控制序列来获得一些可移植性。
  • @tripleee 你是对的。我只希望这是 100% 纯 bash,而不是使用外部命令。
  • 您不需要内联 stty。类似q=$(stty whatever); fun () { echo -n "$q"; }
猜你喜欢
  • 1970-01-01
  • 2016-04-26
  • 2021-03-13
  • 1970-01-01
  • 2021-05-26
  • 2013-08-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多