- 来自 Upsuper 的投稿,原文: http://blog.upsuper.org/advanced-prompt-string-of-bash/
上周六参加了好久没有参加的的 SHLUG 月聚,恰逢 TualatriX 也带团来上海。自由讨论的时候,我看到 TualatriX 的终端十分色彩斑斓,便询问,他给我们展示了他的 bash 的两个特色功能:1、当上一条命令返回结果不为0时显示返回值并高亮显示提示符;2、自动检测git分支。他说这个在他的博客上都可以找到,今天想起来去找了一下,发现了这篇:史上最强的PS1 | I’m TualatriX,感觉满强大的。
不过,说实话,我觉的这个还不够完美,原因有二:一是我发觉高亮显示的时候那个配色相当不怎么样,二是我本来就讨厌提示符太长,这样一下就更长了……于是我就想起 ghosTM 的 zsh 里面有一些信息是放在右边的,我想把返回值也扔右边去,并且是右边上移一行。此外,由于很少使用 git,所以检测 git 分支的功能也就不需要了~
先放一个最终效果图:
然后直接写出了我的新的 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字符长。
到这里也就结束了,然后我们发现,其实看过去很复杂的东西,拆开来还是挺简单的嘛~
11 Responses to 增强版的 Bash 提示符
Zoom.Quiet
三月 1st, 2011 at 9:42 上午
嗯嗯嗯,这么折腾 Bash 还不如直接用 zsh 呢,
PS,俺用 Hg ,所以,也配置出了类似的效果,
可惜无法简单的复用到所有工作终端中,慢慢的,又回到了原始的...
[Reply]
ghosTM55 Reply:
三月 1st, 2011 at 9:46 上午
@Zoom.Quiet, 用zsh的飘过
[Reply]
znetor Reply:
三月 1st, 2011 at 11:27 上午
@ghosTM55,
看到zsh 感觉确实很强大
[Reply]
upsuper Reply:
三月 1st, 2011 at 12:26 下午
@ghosTM55, 唔,暂时不想折腾zsh呢,虽然看过去确实不错……等有时间了把zsh并gentoo一起折腾一下~
[Reply]
riku Reply:
三月 1st, 2011 at 10:54 上午
@Zoom.Quiet, 通常我都用原始的,也不改 shell
。
[Reply]
书痕
三月 1st, 2011 at 10:30 上午
习惯了现在的,估计就算用了彩色的,过过瘾后还会转回基本bash的
[Reply]
Ian
三月 1st, 2011 at 10:59 上午
定义些color的变量就清爽多了。我的加了git branch和rvm的当前版本:
function prompt
{
local WHITE="\[33[1;37m\]"
local GREEN="\[33[0;32m\]"
local CYAN="\[33[0;36m\]"
local GRAY="\[33[0;37m\]"
local BLUE="\[33[0;34m\]"
local RED="\[33[0;31m\]"
local SCREEN_TITLE="33k33\\\\"
export PS1="${GREEN}\u@\h${RED}\$(git branch 2>/dev/null | sed -n 's/^\* \(.*\)$/ (\1)/p')${GRAY}\$(rvm current 2> /dev/null | sed -n 's/..*/ (&)/p') ${CYAN}\w\[33[0m\]
$ "
}
prompt
[Reply]
znetor
三月 1st, 2011 at 11:26 上午
riku 也在场啊 ~ 早知道认识一下嘛
[Reply]
riku Reply:
三月 1st, 2011 at 11:27 上午
@znetor, 我没在啊,这篇是 Upsuper 投稿的。
[Reply]
stufever
三月 1st, 2011 at 9:43 下午
确实挺复杂的
[Reply]
jazzi
三月 2nd, 2011 at 9:17 上午
很棒的修饰,这样区分就很明显了。thanks
[Reply]