Как сменить заголовок xterm

Статические заголовки

Статический заголовок для xterm, color-xterm или rxvt, может быть установлен при помощи ключей -T и -n:

xterm -T "Мой заголовок XTerm" -n "Мой заголовок иконки XTerm"

Динамические заголовки

Для большинства людей более полезны заголовки, отражающие динамическую информацию, такую как имя пользователя или текущий каталог.

escape-последовательности XTerm

Заголовок окна или имя иконки могут быть изменены при помощи escape-последовательностей:

ESC]0;строкаBEL -- Устанавливает строку в качестве имени иконки и заголовка окна

ESC]1;строкаBEL -- Устанавливает имя иконки

ESC]2;строкаBEL -- Устанавливает текст заголовка окна

где ESC - это символ escape (\033), а BEL - это символ bell (\007).Вывод этой последовательности на терминал xterm приведет к изменению заголовка окна или иконки.

Примечание: эти последовательности относятся к терминалу xterm и его производным, таким как nxterm, color-xterm и rxvt. Другие типы терминалов часто используют другие последовательности; см. приложение. Полный список escape-последовательностей для xterm см. в файле ctlseq2.txt, поставляемом вместе с дистрибутивом xterm; или в xterm.seq, поставляемом с дистрибутивом rxvt.

Вывод escape-последовательностей

Для вывода информации, не меняющейся в течении всего сеанса работы - такой, как имя машины или имя пользователя, достаточно поместить в rc-файл следующую строчку:

echo -n "\033]0;${USER}@${HOST}\007"

которая установит заголовок окна в имя_пользователя@имя_машины, если переменные $USER и $HOST установлены правильно. Требуемые опции команды echo зависят от оболочки (см. примеры ниже).Если в течении сеанса заголовок окна должен меняться (напр. текущий каталог), escape-последовательности должны выводиться каждый раз, когда изменяется приглашение оболочки. Некоторые оболочки позволяют вставлять такие последовательности прямо в строку приглашения. Это иллюстрируется в следующем разделе.

Примеры для различных оболочек

Ниже представлены примеры для большинства оболочек - начиная с zsh, который предоставляет для этого специальные средства, и далее в порядке увеличения сложности.

Чтобы убедиться в том, что мы находимся в xterm, мы проверяем переменную $TERM на предмет $TERM=xterm*; шаблон необходим, потому что в некоторых случаях (например для rxvt) она может быть установлена $TERM=xterm-color.

Заметим также, что в производных C-shell, таких как tcsh и csh, неопределенные переменные вызывают фатальную ошибку. Таким образом, перед тем как проверить содержимое переменной $TERM, мы должны проверить ее наличие. Чтобы добиться этого, мы должны использовать:

  if ($?TERM) then
...
endif

(По нашему мнению, это одна из причин, почему не надо использовать C-shell. См. Csh Programming Considered Harmful).Эти примеры можно использовать: просто вставьте их в соответствующий файл инициализации, выполняемый во время запуска интерактивной оболочки. В большинстве случаев он выглядит, примерно как .shellrc (например .zshrc, .tcshrc и т.д.).

zsh

zsh предоставляет несколько функций и расширений, которые мы будем использовать:

precmd ()   функция, выполняемая перед выводом каждого приглашения
chpwd ()    функция, выполняемая после смены текущего каталога
\e          escape-последовательность для символа ESC
\a          escape-последовательность для символа BEL
%n          $USERNAME
%m          имя машины до первой '.'
%~          путь к текущему каталогу, начиная с домашнего

Существует также много других расширений man zshmisc.Таким образом, следующее меняет заголовок XTerm на "имя_пользователя@имя_машины: каталог":

case $TERM in
xterm*)
precmd () {print -Pn "\e]0;%n@%m: %~\a"}
;;
esac

То же самое достигается использованием chpwd() вместо precmd(). Встроенная команда print работает так же, как и echo, но обеспечивает доступ к %-командам.

tcsh

tcsh обладает похожими функциями:

precmd ()   функция, выполняемая перед выводом каждого приглашения
cwdcmd ()   функция, выполняемая после смены текущего каталога
%n          имя пользователя
%m          имя машины
%~          путь к текущему каталогу, начиная с домашнего
%#             '%' для обычных пользователей, '#' для root'а
%{...%}     включает строчку как последовательность escape-сиволов

К сожалению, у tcsh нет аналога 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 устанавливает заголовок "имя_пользователя@имя_машины: каталог". Заметим, что вокруг escape-последовательности стоят символы "%{...%}" (приглашение не должно заканчиваться этим: man tcsh).

bash

bash поддерживает переменную $PROMPT_COMMAND, содержащую команду, запускаемую перед выводом приглашения. Этот пример устанавливает заголовок окна имя_пользователя@имя_машины: каталог:

PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME}: ${PWD}\007"'

где \033 - код символа ESC, а \007 - BEL.Заметим, что кавычки здесь очень важны: значения переменных вставляются в "...", и не вставляются в '...'. Так что переменная $PROMPT_COMMAND устанавливается без подстановки переменных, которая происходит во время выполнения $PROMPT_COMMAND.

Тем не менее, $PWD дает полный путь к каталогу. Если вы хотите, чтобы он начинался с ?, можете воспользоваться следующими расширениями:

\u          $USERNAME
\h          имя машины до первой '.'
\w          каталог, начиная с '~'
\$               '$' для обычных пользователей, '#' для root
\[...\]     вставляет последовательность непечатаемых символов

Таким образом, следующее устанавливает приглашение bash$, а в заголовке XTerm имя_пользователя@имя_машины: каталог:

case $TERM in
xterm*)
PS1="\[\033]0;\u@\h: \w\007\]bash\\$ "
;;
*)
PS1="bash\\$ "
;;
esac

Замечу, что используется последовательность \[...\], которая говорит bash, во время вычисления ширины приглашения игнорировать символы, содержащиеся внутри. Иначе могут возникнуть проблемы с позиционированием курсора во время редактирования строки.

ksh

ksh дает не много возможностей для наших целей, так что нам придется вставить escape-последовательности прямо в приглашение. Следующий пример устанавливает заголовок окна имя_пользователя@имя_машины: каталог и приглашение 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 (они могут быть введены в emacs при помощи C-q ESC и C-q C-g).

csh

В csh это все достаточно сложно:

switch ($TERM)
case "xterm*":
set host=`hostname`
alias cd 'cd \!*; echo -n "^[]0;${user}@$https://adminbook.ru: ${cwd}^Gcsh% "'
breaksw
default:
set prompt='csh% '
breaksw
endsw

где мы переопределяем команду cd для того, чтобы она посылала escape-последовательности. Заметим, что ^[ и ^G - символы ESC и BEL (они могут быть введены в emacs при помощи C-q ESC и C-q C-g).Заметим, что на некоторых системах команда hostname -s выдает короткое имя машины, вместо длинного, а некоторые пользователи могут использовать `pwd` (обратные кавычки запускают команду pwd), вместо $cwd, чтобы получить более точный путь.

Вывод имени текущей задачи

Пользователь часто запускает "долгоиграющие" задачи, такие как top, текстовый редактор, почтовый клиент и т.д., и хочет видеть название задачи в заголовке окна. Это более сложная задача, и она может быть легко решена только в zsh.

zsh

zsh предоставляет идеальную встроенную функцию для этих целей:

preexec()   функция, выполняемая прямо перед запуском команды
$*,$1,...   аргументы, передающиеся в preexec()

Таким образом, мы можем вставить в заголовок имя текущей задачи: case $TERM in
xterm*)
preexec () {
print -Pn "\e]0;$*\a"
}
;;
esac

Заметим, что функуция preexec() появилась в zsh 3.1.2, так что, возможно, вам придется обновить версию.

Другие оболочки

В других оболочках, не имеющих аналога функции preexec(), это осуществить не просто. Если кто-то это сделал, пошлите пример автору этого текста.

Приложение: escape-последовательности для других терминалов

Большинство современных терминалов являются производными xterm или rxvt и поддерживают те же escape-последовательности, которыми мы пользовались раньше. Некоторые фирменные терминалы, идущие вместе с различными версиями Unix, имеют свои собственные escape-последовательности.

IBM aixterm

aixterm понимает escape-последовательности xterm.

SGI wsh, xwsh и winterm

Эти терминалы устанавливают $TERM=iris-ansi и используют escape-последовательности:

ESCP1.yстрокаESC\ Устанавливает заголовок окна

ESCP3.yстрокаESC\ Устанавливает имя иконки

Для полного списка escape-последовательностей xwsh: man 1G xwsh.Терминалы Irix также поддерживают последовательности xterm для установки заголовка окна и имени иконки раздельно, но не для установки и того и другого вместе.

Sun cmdtool и shelltool

cmdtool и shelltool устанавливают $TERM=sun-cmd и используют последовательности:

ESC]lстрокаESC\ Устанавливает заголовок окна

ESC]LстрокаESC\ Устанавливает имя иконки

Это ужасная программа - пользуйтесь чем-нибудь другим.

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&f0kдлинаDстрока Устанавливает строку длиной в качестве заголовка окна

ESC&f-1kдлинаDстрока Устанавливает строку длиной в качестве имени иконки

Простая программа на C, вычисляющая длину строки и выводящая ее, выглядит примерно так:

#include <string.h>
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

Приложение: примеры на других языках

Может быть полезно написать маленькую программу, вставляющую аргумент в заголовок. Ниже представлены несколько примеров.

C

#include <stdio.h>

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";

Благодарности

Хочу выразить благодарность следующим людям, давшим советы, нашедшим опечатки и предоставившим примеры для этого документа.

Paul D. Smith <psmith@BayNetworks.COM> и Christophe Martin <cmartin@ipnl.in2p3.fr> , отметившим неправильные кавычки вокруг $PROMPT_COMMAND в bash. Благодаря этому теперь переменные подставляются правильно.

Paul D. Smith <psmith@BayNetworks.COM> , предложивший использовать \[...\] в приглашении bash для непечатаемых символов.

Christophe Martin <cmartin@ipnl.in2p3.fr> , предоставивший решение для ksh.

Keith Turner <keith@silvaco.com> , предоставивший escape-последовательности для Sun cmdtool и shelltool.

Jean-Albert Ferrez <ferrez@dma.epfl.ch> , указавший на некоторые несовместимости в использовании "PWD" и "$PWD", и использовании "\" с "\\".

Bob Ellison <papillo@hpellis.fc.hp.com> и Jim Searle <jims@broadcom.com>, проверившие dtterm на HP-UX.

Teng-Fong Seak <seak@drfc.cad.cea.fr>, предложивший опцию -s для hostname, использование `pwd`, и использование echo в csh.

Trilia <trilia@nmia.com>, предложивший примеры на других языках.

Brian Miller <bmiller@telstra.com.au>, предложивший escape-последовательности и примеры для hpterm.

Lenny Mastrototaro <lenny@click3x.com>, объяснивший, что терминалы Irix используют последовательности xterm.

Paolo Supino <paolo@init.co.il>, предложивший использовать \\$ в приглашении bash.