在這一章中,我們將會(huì)看一下表面上看來很瑣碎的細(xì)節(jié)-shell 提示符。但這會(huì)揭示一些內(nèi)部 shell 和 終端仿真器的工作方式。
和 Linux 內(nèi)的許多程序一樣,shell 提示符是可高度配置的,雖然我們把它相當(dāng)多地看作是理所當(dāng)然的, 但是我們一旦學(xué)會(huì)了怎樣控制它,shell 提示符是一個(gè)真正有用的設(shè)備。
我們默認(rèn)的提示符看起來像這樣:
[me@linuxbox ~]$
注意它包含我們的用戶名,主機(jī)名和當(dāng)前工作目錄,但是它又是怎樣得到這些東西的呢? 結(jié)果證明非常簡單。提示符是由一個(gè)環(huán)境變量定義的,叫做 PS1(是“prompt string one” 的簡寫)。我們可以通過 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ì)特殊對待這些字符:
序列 | 顯示值 |
---|---|
\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é)束。 |
參照這個(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ù)原提示符,只要簡單地反向操作就可以了。
[me@linuxbox ~]$ PS1="$ps1_old"
現(xiàn)在,我們準(zhǔn)備開始,讓我們看看如果有一個(gè)空的字符串會(huì)發(fā)生什么:
[me@linuxbox ~]$ PS1=
如果我們沒有給提示字符串賦值,那么我們什么也得不到。根本沒有提示字符串!提示符仍然在那里, 但是什么也不顯示,正如我們所要求的那樣。我們將用一個(gè)最小的提示符來代替它:
PS1="\$ "
這樣要好一些。至少能看到我們在做什么。注意雙引號(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)行很長時(shí)間的命令執(zhí)行完后,我們要得到通知。
下一步,讓我們試著創(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í)代,有許多競爭的終端品牌,它們各自工作不同。 它們有著不同的鍵盤,以不同的方式來解釋控制信息。Unix 和類 Unix 的系統(tǒng)有兩個(gè) 相當(dāng)復(fù)雜的子系統(tǒng)來處理終端控制領(lǐng)域的混亂局面(稱為 termcap 和 terminfo)。如果你 查看一下終端仿真器最底層的屬性設(shè)置,可能會(huì)找到一個(gè)關(guān)于終端仿真器類型的設(shè)置。
為了努力使所有的終端都講某種通用語言,美國國家標(biāo)準(zhǔn)委員會(huì)(ANSI)制定了 一套標(biāo)準(zhǔn)的字符序列集合來控制視頻終端。原先 DOS 用戶會(huì)記得 ANSI.SYS 文件, 這是一個(gè)用來使這些編碼解釋生效的文件。
字符顏色是由發(fā)送到終端仿真器的一個(gè)嵌入到了要顯示的字符流中的 ANSI 轉(zhuǎn)義編碼來控制的。 這個(gè)控制編碼不會(huì)“打印”到屏幕上,而是被終端解釋為一個(gè)指令。正如我們在上表看到的字符序列, 這個(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è)屬性可以描繪出“淺”色文本。
序列 | 文本顏色 | 序列 | 文本顏色 |
---|---|---|---|
\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 | 白色 |
讓我們試著制作一個(gè)紅色提示符。我們將在開頭加入轉(zhuǎn)義編碼:
<me@linuxbox ~>$ PS1='\[\033[0;31m\]<\u@\h \W>\$'
<me@linuxbox ~>$
我們的提示符生效了,但是注意我們在提示符之后輸入的文本也是紅色的。為了修改這個(gè)問題, 我們將添加另一個(gè)轉(zhuǎn)義編碼到這個(gè)提示符的末尾來告訴終端仿真器恢復(fù)到原來的顏色。
<me@linuxbox ~>$ PS1='\[\033[0;31m\]<\u@\h \W>\$\[\033[0m\]'
<me@linuxbox ~>$
這看起來要好些!
也有可能要設(shè)置文本的背景顏色,使用下面列出的轉(zhuǎn)義編碼。這個(gè)背景顏色不支持黑體屬性。
\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è)帶有紅色背景的提示符,只是對第一個(gè)轉(zhuǎn)義編碼做個(gè)簡單的修改。
<me@linuxbox ~>$ PS1='\[\033[0;41m\]<\u@\h \W>\$\[\033[0m\] '
<me@linuxbox ~>$
試試這些顏色編碼,看看你能定制出怎樣的提示符!
注意:除了正常的 (0) 和黑體 (1) 字符屬性之外,文本也可以具有下劃線 (4),閃爍 (5), 和反向 (7) 屬性。為了擁有好品味,然而,許多終端仿真器拒絕使用這個(gè)閃爍屬性。
轉(zhuǎn)義編碼也可以用來定位光標(biāo)。這些編碼被普遍地用來,每次當(dāng)提示符出現(xiàn)的時(shí)候,會(huì)在屏幕的不同位置 比如說上面一個(gè)角落,顯示一個(gè)時(shí)鐘或者其它一些信息。這里是一系列用來定位光標(biāo)的轉(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í)鐘(由黃色文本渲染)的紅色長條。提示符的編碼就是這個(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)長條和時(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)建長條。注意雖然一直清空到行末, 但是不改變光標(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è)置。這對文本和背景都起作用。 |
\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
不管你信不信,還有許多事情可以由提示符來完成,涉及到我們在這里沒有論及的 shell 函數(shù)和腳本, 但這是一個(gè)好的開始。并不是每個(gè)人都會(huì)花心思來更改提示符,因?yàn)橥ǔDJ(rèn)的提示符就很讓人滿意。 但是對于我們這些喜歡思考的人們來說,shell 卻提供了許多制造瑣碎樂趣的機(jī)會(huì)。
The Bash Prompt HOWTO 來自于 Linux 文檔工程,對 shell 提示符的用途進(jìn)行了相當(dāng) 完備的論述??稍谝韵骆溄又械玫剑?/p>
Wikipedia 上有一篇關(guān)于 ANSI Escape Codes 的好文章: