下面我们提供了一系列针对一些更常见 Shell 的示例。我们从 zsh
开始,因为它提供了一些工具,使我们的工作更加容易。然后我们将逐步介绍越来越困难的示例。
在所有示例中,我们都测试了环境变量 $TERM
,以确保我们仅将转义符应用于 xterm。我们测试 $TERM=xterm*
;通配符是因为某些变体(例如 rxvt)可能会设置 $TERM=xterm-color
。
我们应该对 C Shell 的衍生版本(如 tcsh
和 csh
)做额外的说明。在 C Shell 中,未定义的变量是致命错误。因此,在测试变量 $TERM
之前,必须先测试其是否存在,以免破坏非交互式 Shell。为了实现这一点,您必须将下面的示例包装在类似如下的内容中:
if ($?TERM) then
...
endif
(我们认为这仅仅是不使用 C Shell 的众多原因之一。请参阅Csh 编程被认为是有害的,以获得有用的讨论)。
下面的示例应通过将它们插入到适当的 Shell 初始化文件中来使用;即在启动时由交互式 Shell 加载的文件。在大多数情况下,这被称为类似 .shellrc
的名称(例如 .zshrc
、.tcshrc
等)。
zsh
提供了一些函数和扩展,我们将使用它们
precmd () a function which is executed just before each prompt
chpwd () a function which is executed whenever the directory is changed
\e escape sequence for escape (ESC)
\a escape sequence for bell (BEL)
%n expands to $USERNAME
%m expands to hostname up to first '.'
%~ expands to directory, replacing $HOME with '~'
还有许多其他的扩展可用:请参阅 zshmisc
的 man 手册。
因此,以下命令会将 xterm 标题设置为“username@hostname: directory
”
case $TERM in
xterm*)
precmd () {print -Pn "\e]0;%n@%m: %~\a"}
;;
esac
这也可以通过使用 chpwd()
而不是 precmd()
来实现。print
内置命令的工作方式类似于 echo
,但允许我们访问 %
提示符转义符。
tcsh
具有一些类似于 zsh
的函数和扩展
precmd () a function which is executed just before each prompt
cwdcmd () a function which is executed whenever the directory is changed
%n expands to username
%m expands to hostname
%~ expands to directory, replacing $HOME with '~'
%# expands to '>' for normal users, '#' for root users
%{...%} includes a string as a literal escape sequence
不幸的是,没有像 zsh
的 print
命令那样的等效命令允许我们在标题字符串中使用提示符转义符,因此我们能做的最好的事情是使用 Shell 变量(在 ~/.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
的 man 手册)。
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
会生成完整的目录路径。如果我们想使用 ~
简写,我们需要将转义字符串嵌入到提示符中,这允许我们利用 Shell 提供的以下提示符扩展
\u expands to $USERNAME
\h expands to hostname up to first '.'
\w expands to directory, replacing $HOME with '~'
\$ expands to '$' for normal users, '#' for root
\[...\] embeds a sequence of non-printing characters
因此,以下命令会生成 bash$
的提示符,以及 username@hostname: directory
的 xterm 标题
case $TERM in
xterm*)
PS1="\[\033]0;\u@\h: \w\007\]bash\\$ "
;;
*)
PS1="bash\\$ "
;;
esac
请注意 \[...\]
的使用,它告诉 bash
在计算提示符的宽度时忽略非打印控制字符。否则,行编辑命令在放置光标时会感到困惑。
ksh
在函数和扩展方面提供的很少,因此我们必须将转义字符串插入到提示符中,以使其动态更新。此示例生成 username@hostname: directory
的标题和 ksh$
的提示符。
case $TERM in
xterm*)
HOST=`hostname`
PS1='^[]0;${USER}@${HOST}: ${PWD}^Gksh$ '
;;
*)
PS1='ksh$ '
;;
esac
但是,$PWD
会生成完整的目录路径。我们可以使用 ${...##...}
构造从目录中删除 $HOME/
的前缀。我们还可以使用 ${...%%...}
截断主机名
HOST=`hostname`
HOST=${HOST%%.*}
PS1='^[]0;${USER}@${HOST}: ${PWD##${HOME}/}^Gksh$ '
请注意,提示符字符串中的 ^[
和 ^G
是 ESC
和 BEL
的单个字符(可以使用 C-q ESC
和 C-q C-g
在 emacs 中输入)。
这在 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
的单个字符(可以使用 C-q ESC
和 C-q C-g
在 emacs 中输入)。
注意:在某些系统上,可以使用 hostname -s
来获取短主机名,而不是完全限定的主机名。某些使用符号链接目录的用户可能会发现 `pwd`
(反引号运行 pwd
命令)比 $cwd
提供更准确的路径。