如何变更 xterm 的主题 Ric Lister, ric@giccs.georgetown.edu 译者: 曾元佑 v2.0, 1999 年十月 27 日 本文解释如何使用溢位序列以动态变更 xterm 的视窗及图示. □例中, 包含数种命令解译器语法. 而附录亦列出给其他类型终端机使用的溢位序列. 可以在哪找到这份文件

这份文件现已是 的一部份, 亦可以在这找到 .

最新版本的各种文件格式都可以在这找到 .

本文取代了原本由 Winfried Trümper 所写的 howto. 静态主题

任一种终端机 xterm, color-xtermrxvt 都可以藉由 -T-n 的参数设定来设定静态主题: xterm -T "My XTerm's Title" -n "My XTerm's Icon Title" 动态主题

许多人发现这在显示一些动态资讯时相当有用, 比如 使用者所登入的主机名称, 现行的工作目录, 等等. xterm 溢位序列

一个已在执行的 xterm 的视窗及图示的主题都可以透过溢位序列来变更. 下列列出跟这个设定有关的序列: ESC]0;stringBEL -- 设定图示及视窗的主题为 string ESC]1;stringBEL -- 设定图示名为 string ESC]2;stringBEL -- 设定视窗主题为 string 在这里 ESC 是指 escape 字元 (\033), 而BEL 是指 bell 字元 (\007).

在 xterm 里头就可以使得视窗及图示的主题变更.

注意: 这些序列可以应用到大部份 xterm 衍生的程式, 比如 nxterm, color-xtermrxvt. 其他的 终端机类型大都使用不一样的序列; 参考附录所列举的□例. 如要参考 xterm 所有的溢位序列参考这个档案 , 这会随著 xterm 的套件发行, 或 , 随著 套件发行. 印出这些序列

有些资讯在命令解译器的生命周期中是自始至终都不变的, 比如 主机名称 及 使用者名称, 那麽在命令解译器的初始启动档 (rc file) 用 echo 指令印出这些字串就够了: echo -n "\033]0;${USER}@${HOST}\007" 应该会产生像 username@hostname 这样的主题, 假设命令解译器的变数 $USER$HOST 都已设定正确的话. echo 所需的选项依命令解译器使用的类型而有所不同 (参考下面的说明).

有些资讯在命令解译器的生命周期中是一直在变的, 比如 现行工作目录, 这些溢位资讯就必须在每次提示字元变化时随著改变. 这下子字串就会在每次你输入命令时更新, 而且你还可以追踪保留 现行目录, 使用者名, 主机名, 等资讯. 部份命令解译器提供这类用途的特殊功能, 有些则没有. 而我们就必须直接插入主题溢位资讯到题示字串中. 这会在下一节中说明 各种命令解译器的□例

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

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

我们要对 C 命令解译器 (C Shell) 族系作特别的注解, 比如 tcshcsh. 在 C 命令解译器, 使用到未定义的变数就算是致命性的错误. 因此, 在测试变数 $TERM 之前, 先确定其是否存在否则会使在非交谈模式下工作的命令解译器停摆. 要达到这个目的你必须把部份东西用下面这种样式包起来: if ($?TERM) then ... endif (我们是不主张用 C 命令解译器 的理由之一. 参考 有很多有用的讨论文章).

以下的□例可以被用来插入到合适的命令解译器的初始启动档; 即 那个在交谈式命令解译器启动时会去读的那个档. 在大部份的情况它被称为 .命令解译器名rc (如 .zshrc, .tcshrc, 等). 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 一样, 但可以让我们去存取 % 命令提示溢位资讯. 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 使用手册说明会更详细). 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 忽略掉非列印的控制字元. 否则行编辑指令将会在移动游标时搞乱掉. 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 输入). 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 更精确的路径名. 显示正在执行的工作名称

通常使用者会启动一个一直在前景执行的工作如 top, 或 一个编辑器, 一个 email 用户端, 等等, 并希望这个工作的名称被显示在 xterm 的主题上. 这是个很棘手的问题且只能在 zsh 环境下可以轻松完成. zsh

zsh 为这种需求提供了一个很理想的内建功能: preexec() 一个在命令执行前一定会叫用的功能函式名 $*,$1,... 传送到 preexec() 的参数 因此, 我们可以用下面的方式把工作名称放到主题上: case $TERM in xterm*) preexec () { print -Pn "\e]0;$*\a" } ;; esac 记住: preexec() 功能函式在 zsh 3.1.2 版出现, 如果你用的版本较旧那就需要更新一下. 其他命令解译器s

对其它缺少 preexec() 函式的命令解译器, 就不是那麽简单了. 如果任何人有□例可以完成同样的工作请把它 email 给作者. 附录: 其它型式终端机的溢位序列

许多新式的终端机都衍生自 xtermrxvt 并且支援我们所使用的溢位序列. 某些专利的终端机随著各种不同的 unix 贩卖则会使用他们字订的溢位序列. IBM aixterm

aixterm 可以辨识出 xterm 的溢位序列. SGI wsh, xwshwinterm

这类终端机会设定 $TERM=iris-ansi 并采用下列的溢位序列: ESCP1.ystringESC\ 设定视窗主题为 string ESCP3.ystringESC\ 设定图示主题为 string xwsh 完整的溢位序列可以参考 xwsh(1G) 的使用说明.

Irix 的终端机支援 xterm 各别设定视窗与图示主题的功能, 但不能两者都设定. Sun cmdtoolshelltool

cmdtoolshelltool 会设定 $TERM=sun-cmd 并采用下列的溢位序列: ESC]lstringESC\ 设定视窗主题为 string ESC]LstringESC\ 设定图示主题为 string 这些真的是很可怕的程式: 尽量别使用. CDE dtterm

dtterm 会设定 $TERM=dtterm, 似乎是可以识别出标准的 xterm 溢位序列及 Sun cmdtool 的溢位序列 (在 Solaris 2.5.1, Digital Unix 4.0, HP-UX 10.20 测试过). HPterm

hpterm 会设定 $TERM=hpterm 并采用下列的溢位序列: ESC&f0klengthDstring 设定视窗主题为长 lengthstring ESC&f-1klengthDstring 设定图示主题为长 lengthstring

一个简单的 C 语言的程式用来计算长度并回应字串, 如下: #include int main(int argc, char *argv[]) { printf("\033&f0k%dD%s", strlen(argv[1]), argv[1]); printf("\033&f-1k%dD%s", strlen(argv[1]), argv[1]); return(0); }

我们也可以写一个小的命令脚本程序, 用 ${#string} (zsh, bash, ksh) 或 ${%string} (tcsh) 的延伸功能来找出字串长度. 以下可用在 zsh: case $TERM in hpterm) str="\e]0;%n@%m: %~\a" precmd () {print -Pn "\e&f0k${#str}D${str}"} precmd () {print -Pn "\e&f-1k${#str}D${str}"} ;; esac 附录: 其它语言的□例

写一个小程式利用 xterm 的溢位功能把传递的参数当成主题. 以下是一些□例. C

#include int main (int argc, char *argv[]) { printf("%c]0;%s%c", '\033', argv[1], '\007'); return(0); } Perl

#!/usr/bin/perl print "\033]0;@ARGV\007"; 铭谢Credits

感谢下列诸位提供建议, 修正, 及□例为本文增色不少.

Paul D. Smith <psmith@BayNetworks.COM> 及 Christophe Martin <cmartin@ipnl.in2p3.fr> 两位指出在 bash $PROMPT_COMMAND 中我引述的错误部份. 正确的方法变化 动态展开的.

Paul D. Smith <psmith@BayNetworks.COM> 建议在 bash 中使用 \[...\] 以整合不能显示的字元.

Christophe Martin <cmartin@ipnl.in2p3.fr> 提供 ksh 的解决方法.

Keith Turner <keith@silvaco.com> 提供给 Sun 的 cmdtoolshelltool 使用的溢位序列.

Jean-Albert Ferrez <ferrez@dma.epfl.ch> 指出在 "PWD" 及 "$PWD" 相矛盾之处, 及 "\" 与 "\\" 的用法.

Bob Ellison <papillo@hpellis.fc.hp.com> 及 Jim Searle <jims@broadcom.com> 测试在 HP-UX 下的 dtterm.

Teng-Fong Seak <seak@drfc.cad.cea.fr> 建议用 -s 选项以 hostname, 使用 `pwd`, 以及 在 csh 下使用 echo .

Trilia <trilia@nmia.com> 建议加入其他语言的用法.

Brian Miller <bmiller@telstra.com.au> 加入 hpterm 的溢位序列与□例.

Lenny Mastrototaro <lenny@click3x.com> 解释 Irix 终端机环境下, 如何使用 xterm 溢位序列.

Paolo Supino <paolo@init.co.il> 建议在 bash 命令列题示下采用 \\$.