Next Previous Contents

4. 各种命令解译器的范例

以下我们提功一些范例给常见的命令解译器使用. 我们先从 zsh 开始, 他可以提供许多灵巧的机制使得我们很容易地完成我们所要的工作. 然后我们再进展到难度较高的范例.

在所有的范例中我们都测试了 $TERM 环境变数. 以确定我们只需把这个溢位信息送到 xterm. 我们对 $TERM=xterm* 做测试; 万用字元是因为些许的差异性 (比如 rxvt) 会设定 $TERM=xterm-color.

我们要对 C 命令解译器 (C Shell) 族系作特别的注解, 比如 tcshcsh. 在 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
%#          对正常的使用者解释为 '>', 而超级使用者则为 '#'
%{...%}     引入一个字串为连续的溢位序列

不幸的, 并没有与 zshprint 相同功能的指令, 能让我们在主题字串中使用提示溢位信息, 因此我们唯一能使用的命令解译器变数 (在 ~/.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 是被设定为一个不被解释的值, 但变数在 "..." 里如果有用到 $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 在命令列字串是单一字元 的 ESCBEL (在 emacs 的环境下可以用 C-q ESCC-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 在命令列字串是单一字元的 ESCBEL (在 emacs 的环境下可以用 C-q ESCC-q C-g 输入).

记住: 在部份的系统 hostname -s 可能会取得较短的 hostname 而不是完整的全名. 部份使用者在有符号连结的目录应该会发现 `pwd` (括起来以确定执行的是 pwd 指令) 可以得到比 $cwd 更精确的路径名.


Next Previous Contents