第13章:自定义提示符
本章中,我们来看一个看似微不足道的细节——shell 提示符。该测试将揭示 shell 和终端模拟器程序的一些内部工作原理。
和 Linux 中多数事物一样,shell 提示符可以高度定制,我们几乎把提示符当作理所当然的,而实际上当我们学会如何控制它的时候,提示符会是一个非常有用的设备。
提示符的解剖
默认提示符看起来是这样的:
注意,它包含了我们的用户名、主机名和当前工作目录,但是它是如何做到的呢?非常简单,事实证明。提示符在名为 PS1
(prompt string 1)的环境变量中被定义。我们可以用 echo
查看 PS1
的内容。
注意:如果你的运行结果和上述示例不符,不用担心。每个 Linux 发行版定义提示符字符串都会有点不一样,有一点别致。
在结果中我们能看到 PS1
包含了一些字符,括号、@符号、$符号,余下的就是迷了。有些聪明人可能认出了这些像如第七章所述的反斜杠转义特殊字符(backslash-escaped special characters)了。表 13-1 提供了部分 bash
在提示符字符串中特别对待的字符清单。
表 13-1:用在 shell 提示符中的转义字符
序列
显示值
\a
ASCII 铃声。遇到该符号,会使计算机蜂鸣。
\d
以星期、月、日格式显示当前日期。如 "Mon May 26"。
\h
本地计算机的主机名去掉尾随域名。
\H
完整的主机名。
\j
当前 shell 会话中正在运行的任务数。
\l
当前终端设备的名称。
\n
换行符。
\r
回车符。
\s
Shell 程序的名称。
\t
以 24 小时制显示的当前时间,格式为:时:分:秒。
\T
以 12 小时制显示的当前时间。
\@
以 12 小时制及 AM/PM 显示的当前时间。
\A
以 24 小时制显示的当前时间,格式为:时:分。
\u
当前用户的用户名。
\v
Shell 的版本号。
\V
Shell 的版本和发行号。
\w
当前工作目录的名称。
\W
当前工作目录最后一部分的名称。
\!
当前命令的历史记录号。
\#
本次 shell 会话中键入的命令次数。
\$
普通用户显示 $
,超级用户显示 #
。
\[
表示一系列一个或多个非打印字符的开始。 这用于嵌入以某种方式操纵终端仿真器的非打印控制字符,例如移动光标或更改文本颜色。
\]
表示非打印字符序列的结尾。
尝试一些可替代的提示符设计
有了这个特殊字符清单,我们就可以改变提示符来看下效果。首先,我们备份现存的提示符字符串,以便随后可以还原。我们可以将现有字符串复制到另一个我们自己创造的 shell 变量中。
我们创建了一个名为 ps1_old
的新变量并将 PS1
变量的值分配给它。可以用 echo
命令验证字符串是否已复制。
在会话期间,可以很方便地用反转顺序还原提示符。
既然我们已经准备好继续了,来看下如果我们有一个空字符串的提示符,会发生什么。
如果我们什么都没分配 给提示符字符串,也将得到空白。完全没有提示符字符!提示符依然在那,但是什么都没显示,恰如我们所要求的那样。这看起来非常令人不安,来把它替换成最简单的提示符。
这样好些。起码我们能看到我们在干嘛了。注意双引号中的后置空格,当显示提示符时,它在美元符号和光标之间留出了一点空间。
来加个蜂鸣到提示符。
现在,每次提示符出现,都可以听到一次蜂鸣,所以有些系统禁用了这个「功能」。这太恼人了,如果我们希望在执行一个特别长时间运行的命令后得到提醒,也会很有用。注意我们包含了 \[
和 \]
转义序列。因为蜂鸣 \a
是非打印字符,即,它不会移动光标,我们需要告诉 bash
以便正确地计算提示符的长度。
接下来,试着做一个有点信息量的提示符,包含主机名和时间信息。
如果我们要保持追踪何时执行了某些任务,在提示符中加入时间会很有用。最后,我们来做个和最初那个提示符差不多样子的。
尝试上面列表中的其它转义字符,看你是否能想出一个有创意的新提示符。
加入颜色
多数终端模拟器程序响应某些非打印字符序列为诸如字符属性(如颜色、粗体文本和可怕的闪烁文本)及光标位置。我们将学一点光标位置,不过首先来看颜色。
终端混乱
回到那个古老的时代,当终端被挂到远程计算机上,有很多竞争品牌的终端,工作方式也各不相同。它们有不同的键盘,对控制信息也有各种不同的解释。Unix 和类 Unix 系统有各种相当复杂的子系统来处理终端控制的混乱(
termcap
和terminfo
)。如果你深入到终端模拟器设置的最底层,将会看到终端模拟器的类型设置。为了让终端能讲一些常用的语言,美国国家标准协会(ANSI)开发了一套字符序列的标准来控制视频终端。过去的 DOS 用户会记得用来解释这些代码的
ANSI.SYS
文件。
字符颜色受控于发送到终端模拟器的 ANSI 转义字符,这些转义字符内嵌在要显示的字符串中。这些控制码不会「打印」在显示器上,而是被终端解释为一个指令。我们看到上表中的 \[
和 \]
序列用来封装非打印字符。以八进制 033
开头的 ANSI 转义码(该码由 Esc
键生成),后面紧跟一个可选的字符属性,跟一个指令。例如,设置文本颜色为正常(属性 = 0)黑色文本,是这样的:
\033[0;30m
表 13-2 列出了可用的文本颜色。请注意,颜色分为两组,通过应用粗体字符属性(1
)进行区分,从而创建「浅色」的外观。
表 13-2:用来设置文本颜色的转义序列
序列
文本颜色
序列
文本颜色
\033[0;30m
黑色
\033[1;30m
暗灰色
\033[0;31m
红色
\033[1;31m
浅红色
\033[0;32m
绿色
\033[1;32m
浅绿色
\033[0;33m
棕色
\033[1;33m
黄色
\033[0;34m
蓝色
\033[1;34m
浅蓝色
\033[0;35m
紫色
\033[1;35m
浅紫色
\033[0;36m
青色
\033[1;36m
浅青色
\033[0;37m
浅灰色
\033[1;37m
白色
来试着做一个红色的提示符。我们在开始处插入一个转义码。
生效了,不过要注意,在提示符后键入的文本也会是红色的。要修正这个错误,需要加另一个转义码到提示符末尾,以便告知终端模拟器回到之前的颜色。
这样就更好了!
同样的,可以用表 13-3 中所列颜色来设置文本背景色,背景色不支持粗体属性。
表 13-3:用来设置背景色的转义序列
序列
背景色
序列
背景色
\033[0;40m
黑色
\033[0;44m
蓝色
\033[0;41m
红色
\033[0;45m
紫色
\033[0;42m
绿色
\033[0;46m
青色
\033[0;43m
棕色
\033[0;47m
浅灰色
我们可以改变第一个转义码来应用红色背景。
可以尝试一下别的颜色,看看你能创造出什么!
注意:除了正常(
0
)和加粗(1
)这两个字符属性之外,文本可以有下划线(4
)、闪烁(5
)和反转(7
)属性。然而,为了良好的品味,许多终端模拟器拒绝遵守闪烁的属性。
移动光标
转义码可以用来定位光标。通常用来提供一个时钟或屏幕上不同位置上的其它信息,如每次都在左上角绘制提示符。表 13-4 列出了光标位置的转义码。
表 13-4:光标移动转义序列
转义码
行为
\033[l;cH
将光标移动到 l
行 c
列
\033[nA
向上移动光标 n
行
\033[nB
向下移动光标 n
行
\033[nC
向前移动光标 n
个字符
\033[nD
向后移动光标 n
个字符
\033[2J
清理屏幕,将光标移动到左上角(0 行 0 列)
\033[K
清理从光标位置到当前行末的字符
\033[s
存储当前光标位置
\033[u
恢复已存储的光标位置
使用表 13-4 中的编码,我们可以构建一个提示符,使得每次显示提示符时,就在屏幕顶端显示一个红色的长条,内含一个时钟(用黄色字体渲染)。提示符的编码是这种看起来吓人的字符串:
表 13-5 给出了这串字符每个部分的功能轮廓。
表 13-5:复杂提示符字符串分解
序列
行为
\[
非打印字符序列开始符号。该符号的用处是让 bash
正确计算可视的提示符尺寸。如果没有精确计算,命令行编辑功能将不能正确地定位光标。
\033[s
存储光标位置。在完成绘制顶部长条和时钟之后,需要返回提示符位置。注意有些终端模拟器不会识别这个代码。
\033[0;0H
将光标移动到左上角,0 行 0 列的位置。
\033[0;41m
设置背景为红色。
\033[K
清理当前光标位置(左上角)到行末。因为当前背景是红色的,行被清理,创建了长条。注意清理到行末不会改变光标位置,它还在左上角。
\033[1;33m
设置文本为黄色。
\t
显示当前时间。这是一个「打印」元素,而我们将其包含在非打印部分中,因为我们不想让 bash
计算时钟的长度。
\033[0m
关闭颜色。影响文本和背景两者。
\033[u
恢复之前保存的光标位置。
\]
非打印字符序列结束符号。
<\u@\h \W>\$
提示符字符串。
保存提示符
显然我们并不想整天都写这种怪物,所以我们想要在某个地方存储我们的提示符。将其添加到 .bashrc
文件,即可永久保存。请将这两行加入到文件中:
总结
无论你是否相信,还有很多通过涉及 shell 函数和脚本的提示符,还有很多可以做到,在此还未涉及,不过此处是个好的开端。不是所有人都很关心改变提示符,因为默认的提示符通常很让人满意了。但是对于我们那些喜欢思考的人呢,shell 提供了好几个小时的休闲乐趣的机会。
扩展阅读
Last updated
Was this helpful?