Next Previous Contents

4. 各種命令解譯器的範例

以下我們提功一些範例給常見的命令解譯器使用. 我們先從 zsh 開始, 他可以提供許多靈巧的機制使得我們很容易地完成我們所要的工作. 然後我們再進展到難度較高的範例.

在所有的範例中我們都測試了 $TERM 環境變數. 以確定我們只需把這個溢位資訊送到 xterm. 我們對 $TERM=xterm* 做測試; 萬用字元是因為些許的差異性 (比如 rxvt) 會設定 $TERM=xterm-color.

我們要對 C 命令解譯器 (C Shell) 族系作特別的註解, 比如 tcsh 及 csh. 在 C 命令解譯器, 使用到未定義的變數就算是致命性的錯誤. 因此, 在測試變數 $TERM 之前, 先確定其是否存在否則會使在非交談模式下工作的命令解譯器停擺. 要達到這個目的你必須把部份東西用下面這種樣式包起來:

  if ($?TERM) then
      ...
  endif
(我們是不主張用 C 命令解譯器 的理由之一. 參考 Csh Programming Considered Harmful 有很多有用的討論文章).

以下的範例可以被用來插入到合適的命令解譯器的初始啟動檔; 即 那個在交談式命令解譯器啟動時會去讀的那個檔. 在大部份的情況它被稱為 .命令解譯器名rc (如 .zshrc, .tcshrc, 等).

4.1 zsh

zsh 提供部份功能與延伸功能, 而我們將用到:

precmd ()   一個在提示命令前必執行的功能含式
chpwd ()    一個在目錄有所變化時會執行的功能含式
\e          溢位字元 (ESC)
\a          bell 的溢位字元 (BEL)
%n          被解釋為 $USERNAME
%m          被解釋為主機名稱在第一個 '.' 之前的部份
%~          被解釋為目錄, 以 '~' 取代 $HOME
更多的延伸功能: 參考 zshmisc 使用手冊.

因此, 以下將設定 xterm 的主題為 "username@hostname: directory":

case $TERM in
    xterm*)
        precmd () {print -Pn "\e]0;%n@%m: %~\a"}
        ;;
esac
這也可以用 chpwd() 取代 precmd() 來達成. print 內建的工作是跟 echo 一樣, 但可以讓我們去存取 % 命令提示溢位資訊.

4.2 tcsh

tcsh 的部份功能含式與延伸功能與 zsh 相同:

precmd ()   一個在提示命令前必執行的功能含式
cwdcmd ()   一個在目錄有所變化時會執行的功能含式
%n          被解釋為 $USERNAME
%m          被解釋為主機名稱
%~          被解釋為目錄, 以 '~' 取代 $HOME
%#          對正常的使用者解釋為 '>', 而超級使用者則為 '#'
%{...%}     引入一個字串為連續的溢位序列

不幸的, 並沒有與 zsh 的 print 相同功能的指令, 能讓我們在主題字串中使用提示溢位資訊, 因此我們唯一能使用的命令解譯器變數 (在 ~/.tcshrc):

switch ($TERM)
    case "xterm*":
        alias precmd 'echo -n "\033]0;${HOST}:$cwd\007"'
        breaksw
endsw
然而, 這會用目錄完整的路徑取代使用 ~. 改把這些字串插入題示字元:
switch ($TERM)
    case "xterm*":
        set prompt="%{\033]0;%n@%m:%~\007%}tcsh%# "
        breaksw
    default:
        set prompt="tcsh%# "
        breaksw
endsw
這會設定 "tcsh% " 的提示字元, 而 xterm 的主題及圖示為 "username@hostname: directory". 要注意 "%{...%}" 必須環繞在溢位序列外 (且不能被放在 提示的最後一項: 參考 tcsh 使用手冊說明會更詳細).

4.3 bash

bash 支援變數 $PROMPT_COMMAND 內含一個指令在提示字元之前執行. 這個範例將設定主題為 username@hostname: directory:

PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME}: ${PWD}\007"'
在這的 \033 是代表字元 ESC, 而 \007 則是 BEL.

記住引號在這相當重要: 會被解釋的變數是放在 "...", 而不是 '...'. 因此 $PROMPT_COMMAND 是被設定為一個不被解釋的值, 但變數在 "..." �埵p果有用到 $PROMPT_COMMAND 則會被解釋.

然而, $PWD 產生完整的路徑. 如果我們要使用 ~ 這種速記, 則我們要把溢位字串嵌入到提示字元, 這會讓我們可以利用命令解譯器所提供的命令列延伸功能:

\u          被解釋為 $USERNAME
\h          被解釋為主機名稱在第一個 '.' 之前的部份
\w          被解釋為目錄, 以 '~' 取代 $HOME
\$          對正常的使用者解釋為 '>', 而超級使用者則為 '#'
\[...\]     嵌入非列印字元溢位序列

因此, 以下將產生 bash$ 的提示字元, 並設定 xterm 的主題為 username@hostname: directory:

case $TERM in
    xterm*)
        PS1="\[\033]0;\u@\h: \w\007\]bash\\$ "
        ;;
    *)
        PS1="bash\\$ "
        ;;
esac
記住 \[...\] 的使用, 在計算題示長度時, 將告訴 bash 忽略掉非列印的控制字元. 否則行編輯指令將會在移動游標時搞亂掉.

4.4 ksh

ksh 幾乎不提供這種方式的功能函式與延伸功能, 因此我們必須插入溢位字串到提示字元中, 使他能動態更新 這個範例將產生主題為 username@hostname: directory 而and a prompt of ksh$ .

case $TERM in
    xterm*)
        HOST=`hostname`
        PS1='^[]0;${USER}@${HOST}: ${PWD}^Gksh$ '
        ;;
    *)
        PS1='ksh$ '
        ;;
esac
而, $PWD 會產生完整的目錄路徑. 我們可以 用 ${...##...} 的方式移去 $HOME/ 的字首. 我們也可以用 ${...%%...} 的方法來截去部份的 hostname:
HOST=`hostname`
HOST=${HOST%%.*}
PS1='^[]0;${USER}@${HOST}: ${PWD##${HOME}/}^Gksh$ '
記住 ^[ 及 ^G 在命令列字串是單一字元 的 ESC 及 BEL (在 emacs 的環境下可以用 C-q ESC 及 C-q C-g 輸入).

4.5 csh

要在 csh 完成同樣的事真的有點困難, 而我們用下面的方式來解決問題:

switch ($TERM)
    case "xterm*":
        set host=`hostname`
        alias cd 'cd \!*; echo -n "^[]0;${user}@${host}: ${cwd}^Gcsh% "'
        breaksw
    default:
        set prompt='csh% '
        breaksw
endsw
麻煩的地方是我們要把 cd 這個指令化身成可送出溢位字串的功能. 記住 ^[ 及 ^G 在命令列字串是單一字元的 ESC 及 BEL (在 emacs 的環境下可以用 C-q ESC 及 C-q C-g 輸入).

記住: 在部份的系統 hostname -s 可能會取得較短的 hostname 而不是完整的全名. 部份使用者在有符號連結的目錄應該會發現 `pwd` (括起來以確定執行的是 pwd 指令) 可以得到比 $cwd 更精確的路徑名.


Next Previous Contents