第13章:自定义提示符

本章中,我们来看一个看似微不足道的细节——shell 提示符。该测试将揭示 shell 和终端模拟器程序的一些内部工作原理。

和 Linux 中多数事物一样,shell 提示符可以高度定制,我们几乎把提示符当作理所当然的,而实际上当我们学会如何控制它的时候,提示符会是一个非常有用的设备。

提示符的解剖

默认提示符看起来是这样的:

[me@linuxbox ~]$

注意,它包含了我们的用户名、主机名和当前工作目录,但是它是如何做到的呢?非常简单,事实证明。提示符在名为 PS1(prompt string 1)的环境变量中被定义。我们可以用 echo 查看 PS1 的内容。

[me@linuxbox ~]$ echo $PS1
[\u@\h \W]\$

注意:如果你的运行结果和上述示例不符,不用担心。每个 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 变量中。

[me@linuxbox ~]$ ps1_old="$PS1"

我们创建了一个名为 ps1_old 的新变量并将 PS1 变量的值分配给它。可以用 echo 命令验证字符串是否已复制。

[me@linuxbox ~]$ echo $ps1_old
[\u@\h \W]\$

在会话期间,可以很方便地用反转顺序还原提示符。

[me@linuxbox ~]$ PS1="$ps1_old"

既然我们已经准备好继续了,来看下如果我们有一个空字符串的提示符,会发生什么。

[me@linuxbox ~]$ PS1=

如果我们什么都没分配 给提示符字符串,也将得到空白。完全没有提示符字符!提示符依然在那,但是什么都没显示,恰如我们所要求的那样。这看起来非常令人不安,来把它替换成最简单的提示符。

PS1="\$ "

这样好些。起码我们能看到我们在干嘛了。注意双引号中的后置空格,当显示提示符时,它在美元符号和光标之间留出了一点空间。

来加个蜂鸣到提示符。

$ PS1="\[\a\]$ "

现在,每次提示符出现,都可以听到一次蜂鸣,所以有些系统禁用了这个「功能」。这太恼人了,如果我们希望在执行一个特别长时间运行的命令后得到提醒,也会很有用。注意我们包含了 \[\] 转义序列。因为蜂鸣 \a 是非打印字符,即,它不会移动光标,我们需要告诉 bash 以便正确地计算提示符的长度。

接下来,试着做一个有点信息量的提示符,包含主机名和时间信息。

$ PS1="\A \h \$ "
17:33 linuxbox $

如果我们要保持追踪何时执行了某些任务,在提示符中加入时间会很有用。最后,我们来做个和最初那个提示符差不多样子的。

17:37 linuxbox $ PS1="<\u@\h \W>\$ "
<me@linuxbox ~>$

尝试上面列表中的其它转义字符,看你是否能想出一个有创意的新提示符。

加入颜色

多数终端模拟器程序响应某些非打印字符序列为诸如字符属性(如颜色、粗体文本和可怕的闪烁文本)及光标位置。我们将学一点光标位置,不过首先来看颜色。

终端混乱

回到那个古老的时代,当终端被挂到远程计算机上,有很多竞争品牌的终端,工作方式也各不相同。它们有不同的键盘,对控制信息也有各种不同的解释。Unix 和类 Unix 系统有各种相当复杂的子系统来处理终端控制的混乱(termcapterminfo)。如果你深入到终端模拟器设置的最底层,将会看到终端模拟器的类型设置。

为了让终端能讲一些常用的语言,美国国家标准协会(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

白色

来试着做一个红色的提示符。我们在开始处插入一个转义码。

red-prompt-1

生效了,不过要注意,在提示符后键入的文本也会是红色的。要修正这个错误,需要加另一个转义码到提示符末尾,以便告知终端模拟器回到之前的颜色。

red-prompt-2

这样就更好了!

同样的,可以用表 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

将光标移动到 lc

\033[nA

向上移动光标 n

\033[nB

向下移动光标 n

\033[nC

向前移动光标 n 个字符

\033[nD

向后移动光标 n 个字符

\033[2J

清理屏幕,将光标移动到左上角(0 行 0 列)

\033[K

清理从光标位置到当前行末的字符

\033[s

存储当前光标位置

\033[u

恢复已存储的光标位置

使用表 13-4 中的编码,我们可以构建一个提示符,使得每次显示提示符时,就在屏幕顶端显示一个红色的长条,内含一个时钟(用黄色字体渲染)。提示符的编码是这种看起来吓人的字符串:

PS1="\[\033[s\033[0;0H\033[0;41m\033[K\033[1;33m\t\033[0m\033[u\]<\u@\h \W>\$ "

表 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 文件,即可永久保存。请将这两行加入到文件中:

PS1="\[\033[s\033[0;0H\033[0;41m\033[K\033[1;33m\t\033[0m\033[u\]<\u@\h \W>\$ "

export PS1

总结

无论你是否相信,还有很多通过涉及 shell 函数和脚本的提示符,还有很多可以做到,在此还未涉及,不过此处是个好的开端。不是所有人都很关心改变提示符,因为默认的提示符通常很让人满意了。但是对于我们那些喜欢思考的人呢,shell 提供了好几个小时的休闲乐趣的机会。

扩展阅读

Last updated

Was this helpful?