鍍金池/ 教程/ Linux/ layout: book-zh title: 自定制 shell 提示符
網(wǎng)絡(luò)系統(tǒng)
打印
重定向
使用命令
位置參數(shù)
權(quán)限
文本處理
疑難排解
layout: book-zh title: 自定制 shell 提示符
查找文件
layout: book-zh title: vi 簡(jiǎn)介
shell 環(huán)境
什么是 shell
編譯程序
鍵盤高級(jí)操作技巧
流程控制:case 分支
流程控制:if 分支結(jié)構(gòu)
layout: book-zh title: 軟件包管理
進(jìn)程
存儲(chǔ)媒介
格式化輸出
編寫第一個(gè) Shell 腳本
啟動(dòng)一個(gè)項(xiàng)目
流程控制:while/until 循環(huán)
文件系統(tǒng)中跳轉(zhuǎn)
字符串和數(shù)字
讀取鍵盤輸入
歸檔和備份
探究操作系統(tǒng)
流程控制:for 循環(huán)
自頂向下設(shè)計(jì)
數(shù)組
操作文件和目錄
奇珍異寶
從 shell 眼中看世界
正則表達(dá)式

layout: book-zh title: 自定制 shell 提示符


layout: book-zh title: 自定制 shell 提示符

在這一章中,我們將會(huì)看一下表面上看來很瑣碎的細(xì)節(jié)-shell 提示符。但這會(huì)揭示一些內(nèi)部 shell 和 終端仿真器的工作方式。

和 Linux 內(nèi)的許多程序一樣,shell 提示符是可高度配置的,雖然我們把它相當(dāng)多地看作是理所當(dāng)然的, 但是我們一旦學(xué)會(huì)了怎樣控制它,shell 提示符是一個(gè)真正有用的設(shè)備。

解剖一個(gè)提示符

我們默認(rèn)的提示符看起來像這樣:

[me@linuxbox ~]$

注意它包含我們的用戶名,主機(jī)名和當(dāng)前工作目錄,但是它又是怎樣得到這些東西的呢? 結(jié)果證明非常簡(jiǎn)單。提示符是由一個(gè)環(huán)境變量定義的,叫做 PS1(是“prompt string one” 的簡(jiǎn)寫)。我們可以通過 echo 命令來查看 PS1的內(nèi)容。

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

注意:如果你 shell 提示符的內(nèi)容和上例不是一模一樣,也不必?fù)?dān)心。每個(gè) Linux 發(fā)行版 定義的提示符稍微有點(diǎn)不同,其中一些相當(dāng)異乎尋常。


從輸出結(jié)果中,我們看到那個(gè) PS1 環(huán)境變量包含一些這樣的字符,比方說中括號(hào),@符號(hào),和美元符號(hào), 但是剩余部分就是個(gè)謎。我們中一些機(jī)敏的人會(huì)把這些看作是由反斜杠轉(zhuǎn)義的特殊字符,就像我們 在第八章中看到的一樣。這里是一部分字符列表,在提示符中 shell 會(huì)特殊對(duì)待這些字符:

表14-1: Shell 提示符中用到的轉(zhuǎn)義字符
序列 顯示值
\a 以 ASCII 格式編碼的鈴聲 . 當(dāng)遇到這個(gè)轉(zhuǎn)義序列時(shí),計(jì)算機(jī)會(huì)發(fā)出嗡嗡的響聲。
\d 以日,月,天格式來表示當(dāng)前日期。例如,“Mon May 26.”
\h 本地機(jī)的主機(jī)名,但不帶末尾的域名。
\H 完整的主機(jī)名。
\j 運(yùn)行在當(dāng)前 shell 會(huì)話中的工作數(shù)。
\l 當(dāng)前終端設(shè)備名。
\n 一個(gè)換行符。
\r 一個(gè)回車符。
\s shell 程序名。
\t 以24小時(shí)制,hours:minutes:seconds 的格式表示當(dāng)前時(shí)間.
\T 以12小時(shí)制表示當(dāng)前時(shí)間。
\@ 以12小時(shí)制,AM/PM 格式來表示當(dāng)前時(shí)間。
\A 以24小時(shí)制,hours:minutes 格式表示當(dāng)前時(shí)間。
\u 當(dāng)前用戶名。
\v shell 程序的版本號(hào)。
\V Version and release numbers of the shell.
\w 當(dāng)前工作目錄名。
\W 當(dāng)前工作目錄名的最后部分。
\! 當(dāng)前命令的歷史號(hào)。
\# 當(dāng)前 shell 會(huì)話中的命令數(shù)。
\$ 這會(huì)顯示一個(gè)"$"字符,除非你擁有超級(jí)用戶權(quán)限。在那種情況下, 它會(huì)顯示一個(gè)"#"字符。
\[ 標(biāo)志著一系列一個(gè)或多個(gè)非打印字符的開始。這被用來嵌入非打印 的控制字符,這些字符以某種方式來操作終端仿真器,比方說移動(dòng)光標(biāo)或者是更改文本顏色。
\] 標(biāo)志著非打印字符序列結(jié)束。

試試一些可替代的提示符設(shè)計(jì)

參照這個(gè)特殊字符列表,我們可以更改提示符來看一下效果。首先, 我們把原來提示符字符串的內(nèi)容備份一下,以備之后恢復(fù)原貌。為了完成備份, 我們把已有的字符串復(fù)制到另一個(gè) shell 變量中,這個(gè)變量是我們自己創(chuàng)造的。

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

我們新創(chuàng)建了一個(gè)叫做 ps1_old 的變量,并把變量 PS1的值賦 ps1_old。通過 echo 命令可以證明 我們的確復(fù)制了 PS1的值。

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

在終端會(huì)話中,我們能在任一時(shí)間復(fù)原提示符,只要簡(jiǎn)單地反向操作就可以了。

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

現(xiàn)在,我們準(zhǔn)備開始,讓我們看看如果有一個(gè)空的字符串會(huì)發(fā)生什么:

[me@linuxbox ~]$ PS1=

如果我們沒有給提示字符串賦值,那么我們什么也得不到。根本沒有提示字符串!提示符仍然在那里, 但是什么也不顯示,正如我們所要求的那樣。我們將用一個(gè)最小的提示符來代替它:

PS1="\$ "

這樣要好一些。至少能看到我們?cè)谧鍪裁?。注意雙引號(hào)中末尾的空格。當(dāng)提示符顯示的時(shí)候, 這個(gè)空格把美元符號(hào)和光標(biāo)分離開。

在提示符中添加一個(gè)響鈴:

$ PS1="\a\$ "

現(xiàn)在每次提示符顯示的時(shí)候,我們應(yīng)該能聽到嗡嗡聲。這會(huì)變得很煩人,但是它可能會(huì) 很有用,特別是當(dāng)一個(gè)需要運(yùn)行很長(zhǎng)時(shí)間的命令執(zhí)行完后,我們要得到通知。

下一步,讓我們?cè)囍鴦?chuàng)建一個(gè)信息豐富的提示符,包含主機(jī)名和當(dāng)天時(shí)間的信息。

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

試試其他上表中列出的轉(zhuǎn)義序列,看看你能否想出精彩的新提示符。

添加顏色

大多數(shù)終端仿真器程序支持一定的非打印字符序列來控制,比方說字符屬性(像顏色,黑體和可怕的閃爍) 和光標(biāo)位置。我們會(huì)更深入地討論光標(biāo)位置,但首先我們要看一下字體顏色。

混亂的終端時(shí)代

回溯到終端連接到遠(yuǎn)端計(jì)算機(jī)的時(shí)代,有許多競(jìng)爭(zhēng)的終端品牌,它們各自工作不同。 它們有著不同的鍵盤,以不同的方式來解釋控制信息。Unix 和類 Unix 的系統(tǒng)有兩個(gè) 相當(dāng)復(fù)雜的子系統(tǒng)來處理終端控制領(lǐng)域的混亂局面(稱為 termcap 和 terminfo)。如果你 查看一下終端仿真器最底層的屬性設(shè)置,可能會(huì)找到一個(gè)關(guān)于終端仿真器類型的設(shè)置。

為了努力使所有的終端都講某種通用語言,美國(guó)國(guó)家標(biāo)準(zhǔn)委員會(huì)(ANSI)制定了 一套標(biāo)準(zhǔn)的字符序列集合來控制視頻終端。原先 DOS 用戶會(huì)記得 ANSI.SYS 文件, 這是一個(gè)用來使這些編碼解釋生效的文件。

字符顏色是由發(fā)送到終端仿真器的一個(gè)嵌入到了要顯示的字符流中的 ANSI 轉(zhuǎn)義編碼來控制的。 這個(gè)控制編碼不會(huì)“打印”到屏幕上,而是被終端解釋為一個(gè)指令。正如我們?cè)谏媳砜吹降淖址蛄校?這個(gè) [ 和 ] 序列被用來封裝這些非打印字符。一個(gè) ANSI 轉(zhuǎn)義編碼以一個(gè)八進(jìn)制033(這個(gè)編碼是由 退出按鍵產(chǎn)生的)開頭,其后跟著一個(gè)可選的字符屬性,在之后是一個(gè)指令。例如,把文本顏色 設(shè)為正常(attribute = 0),黑色文本的編碼如下:

\033[0;30m

這里是一個(gè)可用的文本顏色列表。注意這些顏色被分為兩組,由應(yīng)用程序粗體字符屬性(1) 分化開來,這個(gè)屬性可以描繪出“淺”色文本。

表14-2: 用轉(zhuǎn)義序列來設(shè)置文本顏色
序列 文本顏色 序列 文本顏色
\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 藍(lán)色 \033[1;34m 淺藍(lán)色
\033[0;35m 粉紅 \033[1;35m 淺粉色
\033[0;36m 青色 \033[1;36m 淺青色
\033[0;37m 淺灰色 \033[1;37m 白色

讓我們?cè)囍谱饕粋€(gè)紅色提示符。我們將在開頭加入轉(zhuǎn)義編碼:

<me@linuxbox ~>$ PS1='\[\033[0;31m\]<\u@\h \W>\$'
<me@linuxbox ~>$

我們的提示符生效了,但是注意我們?cè)谔崾痉筝斎氲奈谋疽彩羌t色的。為了修改這個(gè)問題, 我們將添加另一個(gè)轉(zhuǎn)義編碼到這個(gè)提示符的末尾來告訴終端仿真器恢復(fù)到原來的顏色。

<me@linuxbox ~>$ PS1='\[\033[0;31m\]<\u@\h \W>\$\[\033[0m\]'
<me@linuxbox ~>$

這看起來要好些!

也有可能要設(shè)置文本的背景顏色,使用下面列出的轉(zhuǎn)義編碼。這個(gè)背景顏色不支持黑體屬性。

表14-3: 用轉(zhuǎn)義序列來設(shè)置背景顏色
\033[0;40m 藍(lán)色 \033[1;44m 黑色
\033[0;41m 紅色 \033[1;45m 粉紅
\033[0;42m 綠色 \033[1;46m 青色
\033[0;43m 棕色 \033[1;47m 淺灰色

我們可以創(chuàng)建一個(gè)帶有紅色背景的提示符,只是對(duì)第一個(gè)轉(zhuǎn)義編碼做個(gè)簡(jiǎn)單的修改。

<me@linuxbox ~>$ PS1='\[\033[0;41m\]<\u@\h \W>\$\[\033[0m\] '
<me@linuxbox ~>$

試試這些顏色編碼,看看你能定制出怎樣的提示符!


注意:除了正常的 (0) 和黑體 (1) 字符屬性之外,文本也可以具有下劃線 (4),閃爍 (5), 和反向 (7) 屬性。為了擁有好品味,然而,許多終端仿真器拒絕使用這個(gè)閃爍屬性。


移動(dòng)光標(biāo)

轉(zhuǎn)義編碼也可以用來定位光標(biāo)。這些編碼被普遍地用來,每次當(dāng)提示符出現(xiàn)的時(shí)候,會(huì)在屏幕的不同位置 比如說上面一個(gè)角落,顯示一個(gè)時(shí)鐘或者其它一些信息。這里是一系列用來定位光標(biāo)的轉(zhuǎn)義編碼:

表14-4: 光標(biāo)移動(dòng)轉(zhuǎn)義序列
轉(zhuǎn)義編碼 行動(dòng)
\033[l;cH 把光標(biāo)移到第 l 行,第 c 列。
\033[nA 把光標(biāo)向上移動(dòng) n 行。
\033[nB 把光標(biāo)向下移動(dòng) n 行。
\033[nC 把光標(biāo)向前移動(dòng) n 個(gè)字符。
\033[nD 把光標(biāo)向后移動(dòng) n 個(gè)字符。
\033[2J 清空屏幕,把光標(biāo)移到左上角(第零行,第零列)。
\033[K 清空從光標(biāo)位置到當(dāng)前行末的內(nèi)容。
\033[s 存儲(chǔ)當(dāng)前光標(biāo)位置。
\033[u 喚醒之前存儲(chǔ)的光標(biāo)位置。

使用上面的編碼,我們將構(gòu)建一個(gè)提示符,每次當(dāng)這個(gè)提示符出現(xiàn)的時(shí)候,會(huì)在屏幕的上方畫出一個(gè) 包含時(shí)鐘(由黃色文本渲染)的紅色長(zhǎng)條。提示符的編碼就是這個(gè)看起來令人敬畏的字符串:

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

讓我們分別看一下這個(gè)字符串的每一部分所表示的意思:

序列 行動(dòng)
\[ 開始一個(gè)非打印字符序列。其真正的目的是為了讓 bash 能夠正確地計(jì)算提示符的大小。如果沒有這個(gè)轉(zhuǎn)義字符的話,命令行編輯 功能會(huì)弄錯(cuò)光標(biāo)的位置。
\033[s 存儲(chǔ)光標(biāo)位置。這個(gè)用來使光標(biāo)能回到原來提示符的位置, 當(dāng)長(zhǎng)條和時(shí)鐘顯示到屏幕上方之后。當(dāng)心一些 終端仿真器不推崇這個(gè)編碼。
\033[0;0H 把光標(biāo)移到屏幕左上角,也就是第零行,第零列的位置。
\033[0;41m 把背景設(shè)置為紅色。
\033[K 清空從當(dāng)前光標(biāo)位置到行末的內(nèi)容。因?yàn)楝F(xiàn)在 背景顏色是紅色,則被清空行背景成為紅色,以此來創(chuàng)建長(zhǎng)條。注意雖然一直清空到行末, 但是不改變光標(biāo)位置,它仍然在屏幕左上角。
\033[1;33m 把文本顏色設(shè)為黃色。
\t 顯示當(dāng)前時(shí)間。雖然這是一個(gè)可“打印”的元素,但我們?nèi)园阉谔崾痉姆谴蛴〔糠郑?因?yàn)槲覀儾幌?bash 在計(jì)算可見提示符的真正大小時(shí)包括這個(gè)時(shí)鐘在內(nèi)。
\033[0m 關(guān)閉顏色設(shè)置。這對(duì)文本和背景都起作用。
\033[u 恢復(fù)到之前保存過的光標(biāo)位置處。
\] 結(jié)束非打印字符序列。
\$ 提示符字符串。

保存提示符

顯然地,我們不想總是敲入那個(gè)怪物,所以我們將要把這個(gè)提示符存儲(chǔ)在某個(gè)地方。通過把它 添加到我們的.bashrc 文件,可以使這個(gè)提示符永久存在。為了達(dá)到目的,把下面這兩行添加到.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

總結(jié)歸納

不管你信不信,還有許多事情可以由提示符來完成,涉及到我們?cè)谶@里沒有論及的 shell 函數(shù)和腳本, 但這是一個(gè)好的開始。并不是每個(gè)人都會(huì)花心思來更改提示符,因?yàn)橥ǔDJ(rèn)的提示符就很讓人滿意。 但是對(duì)于我們這些喜歡思考的人們來說,shell 卻提供了許多制造瑣碎樂趣的機(jī)會(huì)。

拓展閱讀