在這一章中,我們將會看一下表面上看來很瑣碎的細節(jié)-shell 提示符。但這會揭示一些內(nèi)部 shell 和 終端仿真器的工作方式。
和 Linux 內(nèi)的許多程序一樣,shell 提示符是可高度配置的,雖然我們把它相當多地看作是理所當然的, 但是我們一旦學會了怎樣控制它,shell 提示符是一個真正有用的設備。
我們默認的提示符看起來像這樣:
[me@linuxbox ~]$
注意它包含我們的用戶名,主機名和當前工作目錄,但是它又是怎樣得到這些東西的呢? 結(jié)果證明非常簡單。提示符是由一個環(huán)境變量定義的,叫做 PS1(是“prompt string one” 的簡寫)。我們可以通過 echo 命令來查看 PS1的內(nèi)容。
[me@linuxbox ~]$ echo $PS1
[\u@\h \W]\$
注意:如果你 shell 提示符的內(nèi)容和上例不是一模一樣,也不必擔心。每個 Linux 發(fā)行版 定義的提示符稍微有點不同,其中一些相當異乎尋常。
從輸出結(jié)果中,我們看到那個 PS1 環(huán)境變量包含一些這樣的字符,比方說中括號,@符號,和美元符號, 但是剩余部分就是個謎。我們中一些機敏的人會把這些看作是由反斜杠轉(zhuǎn)義的特殊字符,就像我們 在第八章中看到的一樣。這里是一部分字符列表,在提示符中 shell 會特殊對待這些字符:
| 序列 | 顯示值 |
|---|---|
| \a | 以 ASCII 格式編碼的鈴聲 . 當遇到這個轉(zhuǎn)義序列時,計算機會發(fā)出嗡嗡的響聲。 |
| \d | 以日,月,天格式來表示當前日期。例如,“Mon May 26.” |
| \h | 本地機的主機名,但不帶末尾的域名。 |
| \H | 完整的主機名。 |
| \j | 運行在當前 shell 會話中的工作數(shù)。 |
| \l | 當前終端設備名。 |
| \n | 一個換行符。 |
| \r | 一個回車符。 |
| \s | shell 程序名。 |
| \t | 以24小時制,hours:minutes:seconds 的格式表示當前時間. |
| \T | 以12小時制表示當前時間。 |
| \@ | 以12小時制,AM/PM 格式來表示當前時間。 |
| \A | 以24小時制,hours:minutes 格式表示當前時間。 |
| \u | 當前用戶名。 |
| \v | shell 程序的版本號。 |
| \V | Version and release numbers of the shell. |
| \w | 當前工作目錄名。 |
| \W | 當前工作目錄名的最后部分。 |
| \! | 當前命令的歷史號。 |
| \# | 當前 shell 會話中的命令數(shù)。 |
| \$ | 這會顯示一個"$"字符,除非你擁有超級用戶權(quán)限。在那種情況下, 它會顯示一個"#"字符。 |
| \[ | 標志著一系列一個或多個非打印字符的開始。這被用來嵌入非打印 的控制字符,這些字符以某種方式來操作終端仿真器,比方說移動光標或者是更改文本顏色。 |
| \] | 標志著非打印字符序列結(jié)束。 |
參照這個特殊字符列表,我們可以更改提示符來看一下效果。首先, 我們把原來提示符字符串的內(nèi)容備份一下,以備之后恢復原貌。為了完成備份, 我們把已有的字符串復制到另一個 shell 變量中,這個變量是我們自己創(chuàng)造的。
[me@linuxbox ~]$ ps1_old="$PS1"
我們新創(chuàng)建了一個叫做 ps1_old 的變量,并把變量 PS1的值賦 ps1_old。通過 echo 命令可以證明 我們的確復制了 PS1的值。
[me@linuxbox ~]$ echo $ps1_old
[\u@\h \W]\$
在終端會話中,我們能在任一時間復原提示符,只要簡單地反向操作就可以了。
[me@linuxbox ~]$ PS1="$ps1_old"
現(xiàn)在,我們準備開始,讓我們看看如果有一個空的字符串會發(fā)生什么:
[me@linuxbox ~]$ PS1=
如果我們沒有給提示字符串賦值,那么我們什么也得不到。根本沒有提示字符串!提示符仍然在那里, 但是什么也不顯示,正如我們所要求的那樣。我們將用一個最小的提示符來代替它:
PS1="\$ "
這樣要好一些。至少能看到我們在做什么。注意雙引號中末尾的空格。當提示符顯示的時候, 這個空格把美元符號和光標分離開。
在提示符中添加一個響鈴:
$ PS1="\a\$ "
現(xiàn)在每次提示符顯示的時候,我們應該能聽到嗡嗡聲。這會變得很煩人,但是它可能會 很有用,特別是當一個需要運行很長時間的命令執(zhí)行完后,我們要得到通知。
下一步,讓我們試著創(chuàng)建一個信息豐富的提示符,包含主機名和當天時間的信息。
$ PS1="\A \h \$ "
17:33 linuxbox $
試試其他上表中列出的轉(zhuǎn)義序列,看看你能否想出精彩的新提示符。
大多數(shù)終端仿真器程序支持一定的非打印字符序列來控制,比方說字符屬性(像顏色,黑體和可怕的閃爍) 和光標位置。我們會更深入地討論光標位置,但首先我們要看一下字體顏色。
混亂的終端時代
回溯到終端連接到遠端計算機的時代,有許多競爭的終端品牌,它們各自工作不同。 它們有著不同的鍵盤,以不同的方式來解釋控制信息。Unix 和類 Unix 的系統(tǒng)有兩個 相當復雜的子系統(tǒng)來處理終端控制領域的混亂局面(稱為 termcap 和 terminfo)。如果你 查看一下終端仿真器最底層的屬性設置,可能會找到一個關(guān)于終端仿真器類型的設置。
為了努力使所有的終端都講某種通用語言,美國國家標準委員會(ANSI)制定了 一套標準的字符序列集合來控制視頻終端。原先 DOS 用戶會記得 ANSI.SYS 文件, 這是一個用來使這些編碼解釋生效的文件。
字符顏色是由發(fā)送到終端仿真器的一個嵌入到了要顯示的字符流中的 ANSI 轉(zhuǎn)義編碼來控制的。 這個控制編碼不會“打印”到屏幕上,而是被終端解釋為一個指令。正如我們在上表看到的字符序列, 這個 [ 和 ] 序列被用來封裝這些非打印字符。一個 ANSI 轉(zhuǎn)義編碼以一個八進制033(這個編碼是由 退出按鍵產(chǎn)生的)開頭,其后跟著一個可選的字符屬性,在之后是一個指令。例如,把文本顏色 設為正常(attribute = 0),黑色文本的編碼如下:
\033[0;30m
這里是一個可用的文本顏色列表。注意這些顏色被分為兩組,由應用程序粗體字符屬性(1) 分化開來,這個屬性可以描繪出“淺”色文本。
| 序列 | 文本顏色 | 序列 | 文本顏色 |
|---|---|---|---|
| \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 | 白色 |
讓我們試著制作一個紅色提示符。我們將在開頭加入轉(zhuǎn)義編碼:
<me@linuxbox ~>$ PS1='\[\033[0;31m\]<\u@\h \W>\$'
<me@linuxbox ~>$
我們的提示符生效了,但是注意我們在提示符之后輸入的文本也是紅色的。為了修改這個問題, 我們將添加另一個轉(zhuǎn)義編碼到這個提示符的末尾來告訴終端仿真器恢復到原來的顏色。
<me@linuxbox ~>$ PS1='\[\033[0;31m\]<\u@\h \W>\$\[\033[0m\]'
<me@linuxbox ~>$
這看起來要好些!
也有可能要設置文本的背景顏色,使用下面列出的轉(zhuǎn)義編碼。這個背景顏色不支持黑體屬性。
| \033[0;40m | 藍色 | \033[1;44m | 黑色 |
| \033[0;41m | 紅色 | \033[1;45m | 粉紅 |
| \033[0;42m | 綠色 | \033[1;46m | 青色 |
| \033[0;43m | 棕色 | \033[1;47m | 淺灰色 |
我們可以創(chuàng)建一個帶有紅色背景的提示符,只是對第一個轉(zhuǎn)義編碼做個簡單的修改。
<me@linuxbox ~>$ PS1='\[\033[0;41m\]<\u@\h \W>\$\[\033[0m\] '
<me@linuxbox ~>$
試試這些顏色編碼,看看你能定制出怎樣的提示符!
注意:除了正常的 (0) 和黑體 (1) 字符屬性之外,文本也可以具有下劃線 (4),閃爍 (5), 和反向 (7) 屬性。為了擁有好品味,然而,許多終端仿真器拒絕使用這個閃爍屬性。
轉(zhuǎn)義編碼也可以用來定位光標。這些編碼被普遍地用來,每次當提示符出現(xiàn)的時候,會在屏幕的不同位置 比如說上面一個角落,顯示一個時鐘或者其它一些信息。這里是一系列用來定位光標的轉(zhuǎn)義編碼:
| 轉(zhuǎn)義編碼 | 行動 |
|---|---|
| \033[l;cH | 把光標移到第 l 行,第 c 列。 |
| \033[nA | 把光標向上移動 n 行。 |
| \033[nB | 把光標向下移動 n 行。 |
| \033[nC | 把光標向前移動 n 個字符。 |
| \033[nD | 把光標向后移動 n 個字符。 |
| \033[2J | 清空屏幕,把光標移到左上角(第零行,第零列)。 |
| \033[K | 清空從光標位置到當前行末的內(nèi)容。 |
| \033[s | 存儲當前光標位置。 |
| \033[u | 喚醒之前存儲的光標位置。 |
使用上面的編碼,我們將構(gòu)建一個提示符,每次當這個提示符出現(xiàn)的時候,會在屏幕的上方畫出一個 包含時鐘(由黃色文本渲染)的紅色長條。提示符的編碼就是這個看起來令人敬畏的字符串:
PS1='\[\033[s\033[0;0H\033[0;41m\033[K\033[1;33m\t\033[0m\033[u\]
<\u@\h \W>\$ '
讓我們分別看一下這個字符串的每一部分所表示的意思:
| 序列 | 行動 |
|---|---|
| \[ | 開始一個非打印字符序列。其真正的目的是為了讓 bash 能夠正確地計算提示符的大小。如果沒有這個轉(zhuǎn)義字符的話,命令行編輯 功能會弄錯光標的位置。 |
| \033[s | 存儲光標位置。這個用來使光標能回到原來提示符的位置, 當長條和時鐘顯示到屏幕上方之后。當心一些 終端仿真器不推崇這個編碼。 |
| \033[0;0H | 把光標移到屏幕左上角,也就是第零行,第零列的位置。 |
| \033[0;41m | 把背景設置為紅色。 |
| \033[K | 清空從當前光標位置到行末的內(nèi)容。因為現(xiàn)在 背景顏色是紅色,則被清空行背景成為紅色,以此來創(chuàng)建長條。注意雖然一直清空到行末, 但是不改變光標位置,它仍然在屏幕左上角。 |
| \033[1;33m | 把文本顏色設為黃色。 |
| \t | 顯示當前時間。雖然這是一個可“打印”的元素,但我們?nèi)园阉谔崾痉姆谴蛴〔糠郑?因為我們不想 bash 在計算可見提示符的真正大小時包括這個時鐘在內(nèi)。 |
| \033[0m | 關(guān)閉顏色設置。這對文本和背景都起作用。 |
| \033[u | 恢復到之前保存過的光標位置處。 |
| \] | 結(jié)束非打印字符序列。 |
| \$ | 提示符字符串。 |
顯然地,我們不想總是敲入那個怪物,所以我們將要把這個提示符存儲在某個地方。通過把它 添加到我們的.bashrc 文件,可以使這個提示符永久存在。為了達到目的,把下面這兩行添加到.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ù)和腳本, 但這是一個好的開始。并不是每個人都會花心思來更改提示符,因為通常默認的提示符就很讓人滿意。 但是對于我們這些喜歡思考的人們來說,shell 卻提供了許多制造瑣碎樂趣的機會。
The Bash Prompt HOWTO 來自于 Linux 文檔工程,對 shell 提示符的用途進行了相當 完備的論述??稍谝韵骆溄又械玫剑?/p>
Wikipedia 上有一篇關(guān)于 ANSI Escape Codes 的好文章: