- Sergey Danielyan
- Препарируем Bash Command Shell Prompt
- VT100, terminfo, xterm
- Переменные окружения
- PS1 (Default interaction prompt)
- PS2 (Continuation interactive prompt)
- PS3 (Select related interactive prompt)
- PS4 (Trace related interactive prompt)
- PROMPT_COMMAND
- Основные команды
- Команды общего назначения
- Дата и время
- Параметры терминала
- Рабочая директория
- Нумерация команд
- Раскрашиваем и перемещаем
- Раскрашиваем
- Перемещаем
- Команда tput
- Тюнингуем шелл
- Послесловие
Sergey Danielyan
Препарируем Bash Command Shell Prompt
Сколько времени вы проводите за набором команд в юниксовом терминале, шелле? Если так же много как я (а это каждый рабочий, да и не только, день), то наверняка задумывались о том, как же повысить ее информативность, поменять цветовой и смысловой вывод. Сделать это можно и даже нужно. Сегодня я расскажу о переменных окружения Bash Shell Prompt и для чего они нужны; мы узнаем, что такое терминал, что значат escape последовательности, как правильно их нужно применять, узнаем подробнее о PS переменных и научимся добавлять требуемый функционал в .bashrc файлы. Из всего многообразия escape кодов для терминалов, мы поработаем детально с двумя крупнейшими группами – группа кодов для преобразования внешнего вида текста и группа кодов для позиционирования курсора.
VT100, terminfo, xterm
Начнем с того, что bash, равно как и zsh и другие шеллы являются эмуляторами терминала. Наследие можно проследить от широко известного терминала DEC VT100. VT100 согласно Википедии [1]
… стал для DEC первым массово производимым терминалом, широко использующим графические возможности (мерцание, полужирный шрифт, инвертирование цветов, подчёркивание) и позволяющий выбрать режим отображения 80 или 132 символов в строке. Все настройки VT100 производились при помощи интерактивных диалогов появлявшихся на экране и сохранялись в энергонезависимой памяти, встроенной в сам терминал. В VT100 также имел дополнительный набор псевдографических символов
Что самое главное, терминал управлялся набором команд, называемыми также escape кодами ANSI. И хоть после VT100 были разработаны более продвинутые терминалы, например серии VT200, но набор команд VT100 стал де-фактом для современных эмуляторов. Поэтому далее в тексте будет приниматься за дефолтный тип терминала именно VT100 и отсылка на более детальное описание команд будет идти на ресурсы, где описываются ANSI escape коды именно терминала VT100 как наиболее общеприменимые.
xterm – данным термином называют эмулятор терминала в среде X Window System. Согласно той же Википедии [2]:
xterm была разработана как самостоятельная программа – эмулятор терминала для VAXStation 100 (VX100)
компании DEC. Со временем xterm стал частью X, хоть изначально это и не планировалось. Теперешние шеллы начинали разрабатываться как варианты xterm (в современном терминале есть переменная $TERM, значение которой в bash, zsh как раз и равно “xterm”). Кстати SSH клиент putty также эмулирует xterm.
terminfo (бывший termcap) — база данных описывающая возможности того или иного терминала.
Может показаться, что все шеллы поддерживают строго один тип терминала. Это не так. Какой-нибудь эмулятор может поддерживать vt220 и наследовать другое подмножество команд предоставляемое пользователю. Кстати, в большинстве стартовых скриптов относящихся к работе терминала (шелла), нередка проверка на тип переменной $TERM.
Для получения дополнительной информации советую прочесть больше информации о том терминале, который вы используете. Для получения справочной информации можно воспользоваться командой “man terminfo”.
Переменные окружения
Прежде чем окончательно приступить к главной теме статьи, замечу, что все тесты и настройка переменных проводилась в CentOS 6.1 и для других систем (MacOS, к примеру) написанное может отличаться. Примеры тестировались на bash 4.2.1.
Работа с командной строкой помимо творческого может доставлять и эстетическое удовольствие при использовании (одного знания об их существовании обычно не хватает) нескольких переменных командной строки.
К примеру, вот так выглядит по-умолчанию строка ввода в шелле в CentOS:
Почему именно так? Скоро узнаем. А пока, встречаем: PS1, PS2, PS3, PS4 и PROMPT_COMMAND. Именно PS1 в ответе за внешний вид строки ввода и в основном именно ему и будет посвящен пост. И да, PS от Prompt Statement.
PS1 (Default interaction prompt)
Командная строка ввода. Именно она отвечает за то, что предшествует текущему символу и той команде, которая в данный момент печатается. Пример выше видели? А вот значение PS1 для него:
Вот это и есть содержимое PS1 (закодированное с помощью нескольких символьных последовательностей): открывающая скобка ‘[’, затем имя пользователя (username) [\u], затем символ ‘@’, потом имя хоста (hostname) [\h], текущая директория (workdir) [\W], закрывающая скобка ‘]’ и в конце символ ‘$’ предваряющий командный ввод. Более подробно о PS1 – в следующем разделе.
PS2 (Continuation interactive prompt)
Часто при вводе очень длинной команды (или аргументов) для удобства принято разбивать ее (обычно символом ‘**’) на несколько строк. То, что будет отображено при каждом вводе новой строки, являющейся продолжением предыдущей, содержится в этой переменной окружения. Значение по-умолчанию:
Пример ввода (не судить строго, пример синтетический):
Как видно, символ ‘>’ простой. Попробуем сменить на “next–> ” (в конце пробел):
Удобнее, правда? Хотя кому как…
PS3 (Select related interactive prompt)
Данная строка ввода используется в операторе select shell скрипта. По умолчанию переменная пуста:
Вообще это можно объяснить тем, что значение переменной PS3 имеет смысл лишь в контексте выполнения какой либо программы или скрипта. Давайте рассмотрим в качестве примера небольшой скрипт, печатающий сезон к которому относится выбранный пользователем месяц. Скрипт:
Как видите, значение переменной PS3 задается в первой строке:
и оно используется лишь внутри скрипта. Вот как выглядит работа со скриптом:
Удобно и по делу. А вот как могло бы быть без использования PS3:
Совсем не информативно.
PS4 (Trace related interactive prompt)
Эта переменная определяет внешний вид строки ввода, который появляется при отладке скриптов. Рассмотрим пример (файл ps4-demo.sh):
Два плюса что-то совсем не информативно, не так ли? Предлагаю добавить вывод даты и времени, имени файла и номера строки:
- date +%D.%T.%-3N – формат даты и времени. Более подробнее о синтаксисе прошу вызвать команду man date, а лучше info coreutils ‘date invocation’
- date +%D.%T.%-3N – формат даты и времени. Более подробнее о синтаксисе прошу вызвать команду man date, а лучше info coreutils ‘date invocation’
- $LINENO – номер текущей строки
Теперь в скрипте добавилась команда задания значения PS4. Смотрим:
На порядок информативнее. Теперь можно профилировать выполнение конкретных функций или команд. Вообще для целей дебага bash скриптов существует целый ряд переменных BASH* (таких как BASHPID, BASH_ARG, BASH_LINENO – подробнее о них в bash мануале в разделе “Shell Variables”) или в [3].
PROMPT_COMMAND
Bash выполняет то что написано в данной переменной (это может быть одна команда, а может быть несколько) до вывода PS1. Например, вот так можно заставить shell выводить время перед каждой новой командой:
Если же вывод должен быть в той же строке что и PS1, надо к команде добавить “echo –n” (–n позволит не учитывать завершающий символ перевода строки):
Во всех примерах для изменения переменных, мы использовали ключевое слово export. Но это гарантирует изменения лишь на текущий сеанс. Чтобы изменения вступили в действие на постоянной основе, следует добавить эти команды (export …) в .bashrc и/или .bash_profile, файлы, расположенные, как правило, в домашней директории.
/.bashrc используется при запуске shell’а из уже работающей системы (читается при каждом новом вызове терминала), а
/.bash_profile – при интерактивном входе (логине) в систему (в случае если вход осуществляется не через GUI либо в случае коннекта к системе через SSH), а также при запуске shell’а с опцией “––login” (например, “bash ––login”).
Краткое введение закончено, теперь пора узнать, как использовать эти переменные в шелле максимально эффективно.
Основные команды
Итак, еще раз, вот как выглядит командная строка и отвечающая за нее переменная PS1 на моей CentOS машине:
В PS1 можно записывать не только предопределенные символьные последовательности, но и стандартные команды Linux, также объединяя их через pipe механизм. Все это чуть позже, а пока вот какие последовательности можно использовать (весь список вы сможете найти в разделе “Controlling the Prompt”, я же приведу наиболее интересные и полезные, – в скобках даны примеры как выглядит строка при использовании последовательности):
Команды общего назначения
\h и \H — имя хоста, сокращенное и полное (FQDN, Fully qualified domain name), с учетом домена соответственно:
\u — имя пользователя \n — символ перевода строки \$ — если эффективный идентификатор пользователя (effective UID) равен 0, печатает ‘#’, иначе ‘$’. Пояснение разницы между эффективным UID (eUID) и реальным (rUID) выходит за рамки этого поста, однако вот что следует знать: в обычных случаях eUID не используется, но, например, при попытке запущенного процесса открыть файл, система должна проверить его eUID для возможности или невозможности предоставления разрешения. Вот как выглядит строка при логине обычного пользователя:
А вот так при логине root’ом:
[ и ] — начало и конец вывода непечатаемых символов. Эти символы обязательны при использовании цветовых кодов и при работе с курсором.
\e — ASCII escape символ, кодовое значение которого равно 033; \e идентичен 33. Символ используется в качестве начальной и конечной последовательности при задании цвета.
Дата и время
\d — простой, не настраиваемый формат “Weekday Month Day”:
\D — настраиваемый формат времени и даты, вначале обрабатываемый функцией strftime, с последующей вставкой в shell:
\t — время в формате: 24 часа HH:MM:SS (17:34:23):
\T — время в формате: 12 часов HH:MM:SS (05:34:28):
\@ — время в формате: 12 часов am/pm (05:34 PM):
\A — время в формате: 24 часа HH:MM (17:34):
Параметры терминала
\s — название вызванного шелла, точнее его basename $0 часть (bash):
\v — версия шелла, version (4.1):
\V — полная релизная версия шелла, version + patchlevel (4.1.2):
\l — печатает базовое имя терминального устройства (а по сути его номер), которое в данный момент привязано к шеллу. Что это значит? Рассмотрим на двух примерах:
#1 Терминал запущен в CentOS обычным путем. Запущена команда ps и изменена PS1:
Вывод списка процессов показывает, что имя терминального устройства привязанного к шеллу (т.е. к bash) равно 0: pts/0.
#2 Поднят коннект к CentOS’у по SSH. И вновь ps, затем изменение PS1:
Ну а в этом случае 0 сменился на 3.
Рабочая директория
\W — указывается только текущая директория – имя папки (RE). Домашняя директория обрамлена тильдой ‘
\w — указывается полный путь без последнего слеша у директории (
/sources/RE, /usr/share/X11). Домашняя директория обрамлена тильдой ‘
Нумерация команд
! — выводит под каким номером данная команда будет занесена в истории bash шелла (771,772):
# — выводит под каким номером данная команда в текущем сеансе без привязки к истории. При запуске нового окна терминала, нумерация начнется с единицы:
Вот как этот номер коррелирует с номером команды в истории – сначала номер из истории, потом текущий (644 и 15, 645 и 16):
Краткий список команд, оформленный в виде таблицы:
Помимо перечисленных выше символов-команд, возможен вариант встраивания простых и не очень команд в тело PS. Например, при переходе между директориями, хотелось бы сразу видеть количество файлов и директорий в них:
Ответственные за это две команды: “\$(ls | wc -l)”. Как видите, результат одной команды подается на вход другой; \$ в данном случае не является специальной символьной последовательностью. Также в PS1 можно вставлять пользовательские bash функции. Перепишем пример выше с использованием bash функции:
Если не устраивает написание функций прямо в командной строке, то функцию можно перенести в скрипт (который желательно лежал бы в одной из категорий $PATH) и спокойно использовать в PS1:
Содержимое /usr/bin/count.sh всего одна строчка: ls | wc –l.
Итак, мы рассмотрели самые значимые символьные команды, которые можно использовать для настройки строки ввода преимущественно в bash. О других шеллах чуть позже.
Раскрашиваем и перемещаем
Как было сказано ранее, набор команд (управляющих escape кодов) эмулятора терминала очень большой. Дальше мы посмотрим две группы (два подмножества): это работа с цветом и с курсором/экраном. Список всех возможных команд терминала VT100 можно найти в [5] и [6].
Раскрашиваем
Установка цвета осуществляется с помощью команды:
\e – escape символ (также равен 33, можно использовать вместо \e)
[ – служебный символ
X – код атрибута текста(жирный, подчеркнутый, курсивом): 00=none 01=bold 02=dim/half-bright 04=underscore 05=blink 07=reverse 08=concealed/hidden
Y – цветовой код текста (foreground). Здесь возможны варианты. Можно указать цвет обычной насыщенности (интенсивности): 30=black 31=red 32=green 33=yellow 34=blue 35=magenta 36=cyan 37=white 39=default или тот же цвет, но с повышенной насыщенностью (интенсивностью): 90=black 91=red 92=green 93=yellow 94=blue 95=magenta 96=cyan 97=white
Z – цветовой код фона (background). Здесь также присутствует настройка насыщенности (интенсивности). Фон с цветом обычной насыщенности: 40=black 41=red 42=green 43=yellow 44=blue 45=magenta 46=cyan 47=white 49=default или с повышенной: 100=black 101=red 102=green 103=yellow 104=blue 105=magenta 106=cyan 107=white
m – режим работы с цветом (чуть дальше мы рассмотрим команду работы с курсором, там вместо символа m символ f)
Сброс (если вы не хотите изменить весь вывод шелла – и команды и результат их работы) с помощью команды:
Заметьте, что диапазон цифр, шифрующий либо цвет текста, либо его свойство разный. Это означает, что можно задать как одно значение на позиции X;Y;Z, так и все три значения сразу. Обработка в любом случае будет корректная. Если указана одна цифра и она меньше 30, то значение будет интерпретироваться как свойства текста (bold, blink, пр.), если же величина больше 40, то очевидно, что задан цвет подложки, если же в диапазоне от 30 до 40, это – цвет текста. Если задано недопустимое число для дешифровки (например, 200 или 20), то если для него нет действительных значений, оно просто игнорируется.
Таким образом, наличие отдельных непересекающихся диапазонов для трех категорий однозначно идентифицирует указанное значение.
Перед тем как продолжить, должен обратить внимание на обязательность экранирования escape кодировки в случае использования в prompt строках – для того, чтобы терминал корректно обработал последовательность: корректно посчитал длину строки и осуществил перевод на новую. При использовании кодов вкупе с командой “echo –e” его (экранирование) можно опустить. Экранирование заключается в обрамлении кода, задающего цвет в символы ‘[’ и ‘]’: “\e[1;34m” таким образом, превратится в “[\e[1;34m]”, a “\e[s” соответственно в “[\e[s]“. Далее по тексту в качестве примеров я буду опускать это обрамление, но в конкретных примерах кода оно будет присутствовать.
В качестве тренировки установим жирный (bold=01) шрифт зеленого (foreground green=32) цвета с красным (background red=41) фоном. Кодовая последовательность при этом будет “\e[1;32;41m”:
Если хотите использовать разные цвета для разных участков PS1, вперед, это не запрещено. Но согласитесь, что каждый раз вводить такую громоздкую последовательность довольно утомительно, кроме того, очень легко запутаться. Это вопрос можно легко решить, если вынести определения цветов в отдельный файл и подключить его скажем в .bashrc.
Ниже представлена цветовая таблица возможных сочетаний цвета шрифта и фона полученная с помощью скрипта на [4]. Замечу, что в ней отсутствует цветовая интенсивность (диапазон с 90 по 97 включительно для цвета шрифта и 100-107 для фона текста).
Перемещаем
Команда работы с курсором мало отличается от уже рассмотренной команды работы с цветами. Вот она:
- \e – escape символ (равен 33, можно использовать вместо \e)
- [ – служебный символ
- L – позиция строки (Line)
- C – позиция колонки (Column)
- X – режим работы с курсором:
- H, f – перемещает курсор в заданную позицию на строку L в колонку C
- A – перемещает курсор вверх настрок. Используется только одно значение: \e[LA
- B – перемещает курсор вниз настрок. Используется только одно значение: \e[LB
- C – перемещает курсор вперед наколонок. Используется только одно значение: \e[CC
- D – перемещает курсор назад наколонок. Используется только одно значение: \e[CD
Помимо этих команд для работы с экраном и курсором есть большое количество других [5], например:
- \e[K – стереть линию до конца
- \e[1K – стереть линию с начала до текущей позиции курсора
- \e[2K – Стереть всю линию (позиция курсора останется неизменной)
- \e[J – Очистить экран от курсора вниз
- \e[1J – Очистить экран от курсора вверх
- \e[2J – Очистить весь экран
- \e[s – Сохранить позицию курсора
- \e[u – Восстановить позицию курсора
Имейте в виду, что не все вышеперечисленные команды работают во всех терминалах. Для демонстрации работы этих команд, предлагаю ввести не в переменную PS1, а в виде обычной команды следующую последовательность (идея заимствована из официального руководства Bash-Prompt-HOWTO на сайте [4]):
Что делает команда? Спускаемся на 4 строки вниз (\e[4B), печатаем красным цветом и жирным шрифтом фразу “This is BAAAASH” (\e[1;31mThis is BAAAASH \e[0m) после чего возвращаем позицию курсора обратно: 4 строки вверх (\e[4A) и на длину фразы влево (\e[17D).
Команда tput
В мире существует большое количество языков для работы с терминалами, у каждого набор команд, символьных последовательностей может быть просто огромен. Для общения с ними обычно существует некий промежуточный уровень предоставляющий определенный API. Этот API решает ряд низкоуровневых задач, как то: определение типа терминала, распознавание вводимых команд, поиск соответствия между введенными командами более высокого уровня и наборов кодов, которые поймет конкретный терминал. Одна из этих команд – tput. Изменение цвета через коды довольно неблагодарное занятие. Давайте попробуем переложить эту задачу на плечи tput.
tput — команда, использующая базу данных terminfo для предоставления шеллу терминало-зависимых возможностей. При использовании команды можно указать тип терминала, но обычно это не делается, так как по-умолчанию тип берется из $TERM переменной. Далее мы посмотрим на часто используемые команды.
Имейте в виду, что возможности и функционал команд может отличаться от того же Solaris’а с его ksh: что-то может работать в шелле А, но не работать или работать отлично от ожиданий на терминале Б. Поэтому приведенный ниже обзор не призван перечислить все команды tput, а лишь послужить введением в то, что есть tput и что можно делать с его помощью.
Рассмотрим основные команды tput.
Работа с цветом
0=black 1=red 2=green 3=yellow 4=blue 5=magenta 6=cyan 7=white 9=default
- tput setab [0–7,9] — установить цвет фона используя ANSI escape
- tput setb [0–7,9] — установить цвет фона
- tput setaf [0–7,9] — установить цвет текста используя ANSI escape
- tput setf [0–7,9] — установить цвет текста
Работа с текстом
tput [bold|dim|smul|rmul|rev|sgr0|invis]:
- bold – жирный шрифт
- dim – режим пониженной яркости
- smul – текст подчеркнут
- rmul – завершение режима smul
- rev – инвертирует цвет шрифта
- sgr0 – сброс расширенных атрибутов текста
- invis – текст невидимый
Работа с курсором
- tput cup R C – устанавливает курсор (CUrsor Position) в позицию “Row;Column”
- tput cuf N – двигает курсор (CUrsor Forward) на N позиций вправо
- tput cub N – двигает курсор (CUrsor Back) на N позиций влево
- tput cuu N – двигает курсор (CUrsor Up) на N позиций вверх
- tput cud N – двигает курсор (CUrsor Down) на N позиций вниз
- tput sc – сохраняет позицию курсора
- tput rc – восстанавливает позицию курсора
Получение информации о терминале
- tput lines – количество строк (линий) в терминале
- tput cols – количество колонок в терминале
Очистка экрана
- tput [reset|clear] – очистка экрана, действует похоже команде clear
Имейте в виду, экранирование tput команд происходит по тем же правилам что и обычных escape команд шелла: “\$(tput setaf 2)” при использовании в PS1 превратится в “[\$(tput setaf 2)]”.
Рассмотрим пример использования команды. Выделим жирным зеленым цветом имя пользователя и хост, а жирным красным – текущую директорию:
Тюнингуем шелл
Прежде чем перейти к примерам, необходимо заметить, что для повышения удобочитаемости кода, все цветовые коды были сохранены в файл .col_def и подключены в .bashrc. Коды не экранированы, так что не забывайте добавлять ‘[’ и ‘]’: “[$ANY_COLOR]”. За основу взят файл с [7]. Ссылка на файл в конце статьи. Файл также доступен на pastebin.com.
#1 “Классика со временем”
#2 “Больше информации”
#3 “Директории и файлы”
#4 “True or False. That’s the question”
#5 “Ветви дерева Git”
Послесловие
Рассмотренные примеры, конечно же, далеко не все что можно сделать с интерфейсом командной строки. Читайте мануалы, смотрите форумы, экспериментируйте. Также советую почитать про возможности zsh шелла, у него есть много интересных особенностей, в том числе и в плане тюнинга командной строки. В конце вас ждут ссылки на использованные мною статьи и руководства, а также файлы, упомянутые в тексте. Спасибо, что нашли в себе силы и интерес прочитать эту статью. Если заметили ошибки, неточности, или просто хотите высказать критику, – вэлкам в комменты.
Источник