增强版的 Bash 提示符

上周六参加了好久没有参加的的 SHLUG 月聚,恰逢 TualatriX 也带团来上海。自由讨论的时候,我看到 TualatriX 的终端十分色彩斑斓,便询问,他给我们展示了他的 bash 的两个特色功能:1、当上一条命令返回结果不为0时显示返回值并高亮显示提示符;2、自动检测git分支。他说这个在他的博客上都可以找到,今天想起来去找了一下,发现了这篇:史上最强的PS1 | I’m TualatriX,感觉满强大的。

不过,说实话,我觉的这个还不够完美,原因有二:一是我发觉高亮显示的时候那个配色相当不怎么样,二是我本来就讨厌提示符太长,这样一下就更长了……于是我就想起 ghosTM 的 zsh 里面有一些信息是放在右边的,我想把返回值也扔右边去,并且是右边上移一行。此外,由于很少使用 git,所以检测 git 分支的功能也就不需要了~

先放一个最终效果图: Bash 提示符的最终效果图

然后直接写出了我的新的 PS1

PS1='`a=$?;if [ $a -ne 0 ]; then a="  "$a; echo -ne "\[\e[s\e[1A\e[$((COLUMNS-2))G\e[31m\e[1;41m${a:(-3)}\e[u\]\[\e[0m\e[7m\e[2m\]"; fi`\[\e[1;32m\]\u@\h:\[\e[0m\e[1;34m\]\W\[\e[1;34m\]\$ \[\e[0m\]'

非常复杂唉……让我自己再看一次都头晕……

分解这个提示符

上面看到这个 PS1 写的非常之复杂,不过其实拆解开来也没什么了不起的,只不过看起来蛋疼罢了~

这个 PS1 可以分为两个部分,第一个部分是:

`a=$?;if [ $a -ne 0 ]; then a="  "$a; echo -ne "\[\e[s\e[1A\e[$((COLUMNS-2))G\e[31m\e[1;41m${a:(-3)}\e[u\]\[\e[0m\e[7m\e[2m\]"; fi`

第二个部分是:

\[\e[1;32m\]\u@\h:\[\e[0m\e[1;34m\]\W\[\e[1;34m\]\$ \[\e[0m\]

我们先来研究第二部份,这个部分看起来比较简短。其中我们可以看到一个 PS1 里面非常基本的结构:\u@\h:\W\$ ,这个结构在我的电脑里就显示为 upsuper@upsuper-laptop:~$ 大家大概可以猜到里面是什么意思了吧。

这个基本骨架理出来,剩下的是看过去最蛋疼的那堆莫名其妙的符号了~我们看到很多 \e[ 这样的东西,事实上这个叫做 ANSI 控制码,在 Linux 和 Windows 的命令行里面都是通用的,\e 是 Escape 键的键码,\e[ 是一切 ANSI 控制码的开头。首先来到 \e[1;32m 这个控制码,这表示设置这个符号之后的字符为亮绿色,而 \e[0m 则是清除所有格式,这样看有没有一点清晰了呢?更多用法可以参考维基百科条目ANSI escape code。

之后还有两个东西不清楚,就是 \[\],这两个并不是 ANSI 控制码,而是 Bash 提供的转义符。他们的解释说实话我没看太懂,不过我的理解大概就是,夹在 \[\] 之间的部分 Bash 假定他们的宽度为0,不正确地标注这两个符号会导致 Bash 的换行错误。总之在所有控制符两侧都加上这两个就对了~

第二个部分解决了,下面来看蛋疼的第一部份

`a=$?;if [ $a -ne 0 ]; then a="  "$a; echo -ne "\[\e[s\e[1A\e[$((COLUMNS-2))G\e[31m\e[1;41m${a:(-3)}\e[u\]\[\e[0m\e[7m\e[2m\]"; fi`

很明显,整个结构被一个正引号引起来,表示执行并返回其中的结果。这样我们就可以把这个部分分解开来了:

a=$?
if [ $a -ne 0 ]; then
    a="  "$a
    echo -ne "\[\e[s\e[1A\e[$((COLUMNS-2))G\e[31m\e[1;41m${a:(-3)}\e[u\]\[\e[0m\e[7m\e[2m\]"
fi

稍微懂点编程就会觉得这也没什么技术含量嘛,其中 $? 就是上一个程序运行的返回值,我们获取并判断他,如果不为零就进行下面的操作。a=" "$a 这句我们待会而再看,先看下面那个 echo -ne 的语句。echo 我们知道是显示字符串,而 -ne 实际上是两个参数 -n-e-n 表示输出字符串后不输出换行符,-e 表示解析后面的转义符。

最后就剩分析那个打印的东西了。我们发现主要部分其实和上面是一样的,无非就是一些设置格式的事情,我们去掉格式设置,发现主要是这样的:

\e[s\e[1A\e[$((COLUMNS-2))G${a:(-3)}\e[u

这个部分仍然包含许多 ANSI 控制符,第一个是 \e[s,表示保存当前光标位置,与最后一个表示恢复光标位置的控制符 \e[u 遥相呼应,由于我们需要大规模移动光标,所以我们要备份一下位置。然后我们看到 \e[1A,这个控制符表示将光标上移一行。然后之后有一个很复杂的东西 \e[$((COLUMNS-2))G,这个对应的控制符是 \e[*G,表示设置光标到第几列,而 $((COLUMNS-2)) 表示这个列数为当前可显示的最大列数-2。后面有一个 ${a:(-3)},也就是取前面的后三位显示(返回值的范围是0-255)。

现在我们回到前面的 a=" "$a,发现这个的目的其实是和 ${a:(-3)} 对应,让这个部分无论如何保证有三个字符可以出现。事实上最初我并不是这么写的,而是写 $((COLUMNS-${#a}+1)),表示 $a 有多长就显示多长。但这样感觉不美观,就改成了固定3字符长。

到这里也就结束了,然后我们发现,其实看过去很复杂的东西,拆开来还是挺简单的嘛~

参考资料

Comments !

social