如何让终端提示符扩展到终端的宽度?

7
我注意到在这个视频中,终端提示符在换行之前会延伸到整个终端的宽度。我该如何设置我的PS1变量以用某些字符填充剩余的终端空间,就像这位用户所做的那样?
问题是,我不知道如何根据每个命令更新PS1变量。在我看来,PS1的字符串值只读取一次,就像.bashrc文件只读取一次一样。我需要在每个命令后写一些钩子吗?
我还应该指出,PS1变量将根据组成它的转义字符而被评估为不同的长度。例如,\w打印路径。
我知道可以使用$(COLUMNS)获取终端宽度,并使用${#PS1}获取当前PS1变量的宽度,进行计算并打印正确数量的缓冲字符,但是如何使它在每次更新时都能更新?有推荐的方法吗?
2个回答

7

假设你希望你的提示符看起来像这样:

left text----------------------------------------------------------right text
prompt$ 

如果right text的大小是已知的,这个过程就比较简单了。(例如,它可能是当前的日期和时间。)我们需要做的是打印出正确数量的短横线(或者对于utf-8终端,更漂亮的\u2500),接着是right text,然后是一个回车符(\r,而不是换行符),最后是左侧的文本,它将覆盖掉短横线。唯一棘手的部分是“正确数量的短横线”,但是我们可以使用$(tput cols)来查看终端的宽度,幸运的是,bash将会展开PS1。因此,例如:

PS1='\[$(printf "%*s" $(($(tput cols)-20)) "" | sed "s/ /-/g") \d \t\r\u@\h:\w \]\n\$ '

这里的$(($(tput cols)-20))是终端宽度减去20,这基于\d \t恰好为20个字符宽(包括初始空格)。 PS1无法理解utf-8转义符号(\uxxxx),将相应替换插入到sed命令中涉及麻烦的嵌套引号问题,虽然有可能。但是,printf能够理解utf-8转义符号,因此更容易以不同的方式生成破折号序列:
PS1='\[$(printf "\\u2500%.0s" $(seq 21 $(tput cols))) \d \t\r\u@\h:\w \]\n\$ '

另一种方法是关闭终端的自动换行功能,如果您使用的是xterm或实现了相同控制代码的终端模拟器(或Linux控制台本身),则可能实现此功能。要禁用自动换行,请输出序列ESC[?7l。要重新启用,请使用ESC[?7h。当自动换行被禁用时,一旦输出达到一行的末尾,最后一个字符将只是被下一个字符覆盖而不是开始一个新行。使用这种技术,不需要计算破折号序列的确切长度; 我们只需要一个比任何控制台都宽的破折号字符串,例如以下内容:
DASHES="$(printf '\u2500%0.s' {1..1000})"
PS1='\[\e[?7l\u@\h:\w $DASHES \e[19D \d \t\e[?7h\]\n\$ '

这里,\e[19D是“将光标向后移动19个字符”的终端仿真器代码。我也可以使用$(tput cub 19)。 (可能有一个tput参数用于打开和关闭自动换行,但我不知道它是什么。)
视频中的示例还涉及在实际命令行中插入右对齐字符串。我不知道使用bash有任何干净的方法; 视频中的控制台几乎肯定使用了带有RPROMPT功能的zsh。当然,您可以使用与上面相同的技术在bash中输出右对齐提示,但是readline不会知道它们的存在,因此一旦您执行某些操作来编辑该行,右提示将消失。

3
使用PROMPT_COMMAND在每个命令前重设PS1的值。
PROMPT_COMMAND=set_prompt
set_prompt () {
    PS1=...
}

尽管某些系统脚本(或您自己)可能已经使用PROMPT_COMMAND完成某些任务,如果是这种情况,您只需要添加即可。
PROMPT_COMMAND="$PROMPT_COMMAND; set_prompt"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接