如何变更 xterm 的主题 Ric Lister, ric@giccs.georgetown.edu 译者: 曾元佑 [1]yytseng@ms16.hinet.net v2.0, 1999 年十月 27 日 _________________________________________________________________ 本文解释如何使用溢位序列以动态变更 xterm 的视窗及图示. □例中, 包含数种 命令解译器语法. 而附录亦列出给其他类型终端机使用的溢位序列. _________________________________________________________________ 1. 可以在哪找到这份文件 2. 静态主题 3. 动态主题 * 3.1 xterm 溢位序列 * 3.2 印出这些序列 4. 各种命令解译器的□例 * 4.1 zsh * 4.2 tcsh * 4.3 bash * 4.4 ksh * 4.5 csh 5. 显示正在执行的工作名称 * 5.1 zsh * 5.2 其他命令解译器s 6. 附录: 其它型式终端机的溢位序列 * 6.1 IBM aixterm * 6.2 SGI wsh, xwsh 及 winterm * 6.3 Sun cmdtool 及 shelltool * 6.4 CDE dtterm * 6.5 HPterm 7. 附录: 其它语言的□例 * 7.1 C * 7.2 Perl 8. 铭谢Credits _________________________________________________________________ 1. 可以在哪找到这份文件 这份文件现已是 [2]Linux HOWTO Index 的一部份, 亦可以在这找到 [3]http://sunsite.unc.edu/LDP/HOWTO/mini/Xterm-Title.html. 最新版本的各种文件格式都可以在这找到 [4]http://www.giccs.georgetown.edu/~ric/howto/Xterm-Title/. 本文取代了原本由 Winfried Trümper 所写的 howto. 2. 静态主题 任一种终端机 xterm, color-xterm 或 rxvt 都可以藉由 -T 及 -n 的参数设定 来设定静态主题: xterm -T "My XTerm's Title" -n "My XTerm's Icon Title" 3. 动态主题 许多人发现这在显示一些动态资讯时相当有用, 比如 使用者所登入的主机名称, 现行的工作目录, 等等. 3.1 xterm 溢位序列 一个已在执行的 xterm 的视窗及图示的主题都可以透过溢位序列来变更. 下列列 出跟这个设定有关的序列: * ESC]0;stringBEL -- 设定图示及视窗的主题为 string * ESC]1;stringBEL -- 设定图示名为 string * ESC]2;stringBEL -- 设定视窗主题为 string 在这里 ESC 是指 escape 字元 (\033), 而BEL 是指 bell 字元 (\007). 在 xterm 里头就可以使得视窗及图示的主题变更. 注意: 这些序列可以应用到大部份 xterm 衍生的程式, 比如 nxterm, color-xterm 及 rxvt. 其他的 终端机类型大都使用不一样的序列; 参考附录所 列举的□例. 如要参考 xterm 所有的溢位序列参考这个档案 [5]ctlseq2.txt, 这会随著 xterm 的套件发行, 或 [6]xterm.seq, 随著 [7]rxvt 套件发行. 3.2 印出这些序列 有些资讯在命令解译器的生命周期中是自始至终都不变的, 比如 主机名称 及 使 用者名称, 那麽在命令解译器的初始启动档 (rc file) 用 echo 指令印出这些字 串就够了: echo -n "\033]0;${USER}@${HOST}\007" 应该会产生像 username@hostname 这样的主题, 假设命令解译器的变数 $USER 及 $HOST 都已设定正确的话. echo 所需的选项依命令解译器使用的类型而有所 不同 (参考下面的说明). 有些资讯在命令解译器的生命周期中是一直在变的, 比如 现行工作目录, 这些溢 位资讯就必须在每次提示字元变化时随著改变. 这下子字串就会在每次你输入命 令时更新, 而且你还可以追踪保留 现行目录, 使用者名, 主机名, 等资讯. 部份 命令解译器提供这类用途的特殊功能, 有些则没有. 而我们就必须直接插入主题 溢位资讯到题示字串中. 这会在下一节中说明 4. 各种命令解译器的□例 以下我们提功一些□例给常见的命令解译器使用. 我们先从 zsh 开始, 他可以提 供许多灵巧的机制使得我们很容易地完成我们所要的工作. 然後我们再进展到难 度较高的□例. 在所有的□例中我们都测试了 $TERM 环境变数. 以确定我们只需把这个溢位资讯 送到 xterm. 我们对 $TERM=xterm* 做测试; 万用字元是因为些许的差异性 (比 如 rxvt) 会设定 $TERM=xterm-color. 我们要对 C 命令解译器 (C Shell) 族系作特别的注解, 比如 tcsh 及 csh. 在 C 命令解译器, 使用到未定义的变数就算是致命性的错误. 因此, 在测试变数 $TERM 之前, 先确定其是否存在否则会使在非交谈模式下工作的命令解译器停 摆. 要达到这个目的你必须把部份东西用下面这种样式包起来: if ($?TERM) then ... endif (我们是不主张用 C 命令解译器 的理由之一. 参考 [8]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 是被设定为一个不被解释的值, 但变数在 "..." □如果有用到 $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 更精确的路径名. 5. 显示正在执行的工作名称 通常使用者会启动一个一直在前景执行的工作如 top, 或 一个编辑器, 一个 email 用户端, 等等, 并希望这个工作的名称被显示在 xterm 的主题上. 这是个 很棘手的问题且只能在 zsh 环境下可以轻松完成. 5.1 zsh zsh 为这种需求提供了一个很理想的内建功能: preexec() 一个在命令执行前一定会叫用的功能函式名 $*,$1,... 传送到 preexec() 的参数 因此, 我们可以用下面的方式把工作名称放到主题上: case $TERM in xterm*) preexec () { print -Pn "\e]0;$*\a" } ;; esac 记住: preexec() 功能函式在 zsh 3.1.2 版出现, 如果你用的版本较旧那就需要 更新一下. 5.2 其他命令解译器s 对其它缺少 preexec() 函式的命令解译器, 就不是那麽简单了. 如果任何人有□ 例可以完成同样的工作请把它 email 给作者. 6. 附录: 其它型式终端机的溢位序列 许多新式的终端机都衍生自 xterm 或 rxvt 并且支援我们所使用的溢位序列. 某 些专利的终端机随著各种不同的 unix 贩卖则会使用他们字订的溢位序列. 6.1 IBM aixterm aixterm 可以辨识出 xterm 的溢位序列. 6.2 SGI wsh, xwsh 及 winterm 这类终端机会设定 $TERM=iris-ansi 并采用下列的溢位序列: * ESCP1.ystringESC\ 设定视窗主题为 string * ESCP3.ystringESC\ 设定图示主题为 string 而 xwsh 完整的溢位序列可以参考 xwsh(1G) 的使用说明. Irix 的终端机支援 xterm 各别设定视窗与图示主题的功能, 但不能两者都设 定. 6.3 Sun cmdtool 及 shelltool cmdtool 及 shelltool 会设定 $TERM=sun-cmd 并采用下列的溢位序列: * ESC]lstringESC\ 设定视窗主题为 string * ESC]LstringESC\ 设定图示主题为 string 这些真的是很可怕的程式: 尽量别使用. 6.4 CDE dtterm dtterm 会设定 $TERM=dtterm, 似乎是可以识别出标准的 xterm 溢位序列及 Sun cmdtool 的溢位序列 (在 Solaris 2.5.1, Digital Unix 4.0, HP-UX 10.20 测试过). 6.5 HPterm hpterm 会设定 $TERM=hpterm 并采用下列的溢位序列: * ESC&f0klengthDstring 设定视窗主题为长 length 的 string * ESC&f-1klengthDstring 设定图示主题为长 length 的 string 一个简单的 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 7. 附录: 其它语言的□例 写一个小程式利用 xterm 的溢位功能把传递的参数当成主题. 以下是一些□例. 7.1 C #include int main (int argc, char *argv[]) { printf("%c]0;%s%c", '\033', argv[1], '\007'); return(0); } 7.2 Perl #!/usr/bin/perl print "\033]0;@ARGV\007"; 8. 铭谢Credits 感谢下列诸位提供建议, 修正, 及□例为本文增色不少. Paul D. Smith 及 Christophe Martin 两位指出在 bash $PROMPT_COMMAND 中我引述的错误 部份. 正确的方法变化 是 动态展开的. Paul D. Smith 建议在 bash 中使用 \[...\] 以整 合不能显示的字元. Christophe Martin 提供 ksh 的解决方法. Keith Turner 提供给 Sun 的 cmdtool 及 shelltool 使 用的溢位序列. Jean-Albert Ferrez 指出在 "PWD" 及 "$PWD" 相矛盾之 处, 及 "\" 与 "\\" 的用法. Bob Ellison 及 Jim Searle 测试在 HP-UX 下的 dtterm. Teng-Fong Seak 建议用 -s 选项以 hostname, 使用 `pwd`, 以及 在 csh 下使用 echo . Trilia 建议加入其他语言的用法. Brian Miller 加入 hpterm 的溢位序列与□例. Lenny Mastrototaro 解释 Irix 终端机环境下, 如何使用 xterm 溢位序列. Paolo Supino 建议在 bash 命令列题示下采用 \\$. References 1. mailto:yytseng@ms16.hinet.net 2. http://sunsite.unc.edu/LDP/HOWTO/ 3. http://sunsite.unc.edu/LDP/HOWTO/mini/Xterm-Title.html 4. http://www.giccs.georgetown.edu/~ric/howto/Xterm-Title/ 5. http://www.giccs.georgetown.edu/~ric/howto/Xterm-Title/ctlseq2.txt 6. http://www.giccs.georgetown.edu/~ric/howto/Xterm-Title/xterm.seq 7. http://www.rxvt.org/ 8. http://language.perl.com/versus/csh.whynot