Цветное многострочное приглашение командной строки

Приглашение командной строки для администраторов Linux вещь, которая находится перед глазами гораздо чаще, чем, например, обои рабочего стола. Рекомендации по изменения цвета приглашения для отличия одного сервера от другого при работе по ssh существуют давно. Но есть способ сделать приглашение ещё более функциональным и удобным в использовании. Например, моё приглашение занимает целых 3 строки. Но это совсем не громоздко, как может показаться на первый взгляд. Попробуйте ввести в терминале вот эту команду:

export PS1="\[\e[0;36m\]┌──\[\e[0m\][ \[\e[0;33m\]\u\[\e[0m\]\[\e[0;32m\]@\[\e[0;36m\]\h\[\e[0m\] ] [ \[\e[0;36m\]\t\[\e[0m\] ]\n\[\e[0;36m\]├── \[\e[0;32m\]\w\[\e[0;36m\]\n\[\e[0;36m\]└>\[\e[0m\]"

Это временно изменит ваше приглашение на трёхстрочное и вы сможете попробовать его в деле. Сделайте пару переходов по каталогам. Оцените удобство «длинной» версии пути до текущего каталога у вас перед глазами. Понравилось? Тогда добро пожаловать в статью, там всё разложено по полочкам. Ну а если не понравилось, просто закройте терминал. После перезапуска восстановится ваше обычное приглашение командной строки.

Вводная информация

Приглашение командной строки определяется переменной среды PS1. Если ввести команду echo $PS1, можно увидеть текущую конфигурацию приглашения. В приглашении используются специальные макросы, вроде: \h-имя хоста или \t-текущее время. Однако, в сущности переменная просто определяет шаблон строки, которая выводится терминалом при переводе каретки на новую строку. Поэтому, мы можем внутри приглашения использовать команду перевода каретки, стандартные модификаторы bash для изменения цвета и даже символы юникода. При открытии нового окна терминала, оболочка загружает файл ~/.bashrc, в котором содержатся переменные окружения. Если мы поместим в конец файла строку, определяющую переменную PS1, то при запуске терминала переменная будет загружена в окружение и мы увидим модифицированное приглашение.

Цвет

Цвет строки в bash задаётся escape последовательностями, которые должны следовать перед строкой. Мониторы уже давно отображают цветов больше, чем человеческий глаз может различить, но в bash’е исторически существует поддержка всего восьми: Black, Red, Green, Yellow, Blue, Purple, Cyan. Последовательность задаётся с дополнительным модификатором, который определяет начертание символов и интенсивность цвета. Есть последовательность для изменения цвета фона за символами (только за символами, не всего терминала). Ещё есть специальный модификатор, отменяющий действие всех предыдущих. Если не завершить строку таким модификатором, изменение цвета затронет всю строку до конца. Для использования цветов в консоли удобно создать файлик в домашнем каталоге и назвать его, например, .bash_colors. В файлике мы зададим переменные с понятными названиями, упрощающими использование цветового выделения. Итак, создайте файл ~/.bash_colors и наполните его следующим содержимым:

# Сброс
RstColor='\e[0m'       # Text Reset

# Обычные цвета
Black='\e[0;30m'        # Black
Red='\e[0;31m'          # Red
Green='\e[0;32m'        # Green
Yellow='\e[0;33m'       # Yellow
Blue='\e[0;34m'         # Blue
Purple='\e[0;35m'       # Purple
Cyan='\e[0;36m'         # Cyan
White='\e[0;37m'        # White

# Жирные
BBlack='\e[1;30m'       # Black
BRed='\e[1;31m'         # Red
BGreen='\e[1;32m'       # Green
BYellow='\e[1;33m'      # Yellow
BBlue='\e[1;34m'        # Blue
BPurple='\e[1;35m'      # Purple
BCyan='\e[1;36m'        # Cyan
BWhite='\e[1;37m'       # White

# Подчёркнутые
UBlack='\e[4;30m'       # Black
URed='\e[4;31m'         # Red
UGreen='\e[4;32m'       # Green
UYellow='\e[4;33m'      # Yellow
UBlue='\e[4;34m'        # Blue
UPurple='\e[4;35m'      # Purple
UCyan='\e[4;36m'        # Cyan
UWhite='\e[4;37m'       # White

# Фоновые
On_Black='\e[40m'       # Black
On_Red='\e[41m'         # Red
On_Green='\e[42m'       # Green
On_Yellow='\e[43m'      # Yellow
On_Blue='\e[44m'        # Blue
On_Purple='\e[45m'      # Purple
On_Cyan='\e[46m'        # Cyan
On_White='\e[47m'       # White

# Высоко Интенсивные
IBlack='\e[0;90m'       # Black
IRed='\e[0;91m'         # Red
IGreen='\e[0;92m'       # Green
IYellow='\e[0;93m'      # Yellow
IBlue='\e[0;94m'        # Blue
IPurple='\e[0;95m'      # Purple
ICyan='\e[0;96m'        # Cyan
IWhite='\e[0;97m'       # White

# Жирные Высоко Интенсивные
BIBlack='\e[1;90m'      # Black
BIRed='\e[1;91m'        # Red
BIGreen='\e[1;92m'      # Green
BIYellow='\e[1;93m'     # Yellow
BIBlue='\e[1;94m'       # Blue
BIPurple='\e[1;95m'     # Purple
BICyan='\e[1;96m'       # Cyan
BIWhite='\e[1;97m'      # White

# Высоко Интенсивные фоновые
On_IBlack='\e[0;100m'   # Black
On_IRed='\e[0;101m'     # Red
On_IGreen='\e[0;102m'   # Green
On_IYellow='\e[0;103m'  # Yellow
On_IBlue='\e[0;104m'    # Blue
On_IPurple='\e[0;105m'  # Purple
On_ICyan='\e[0;106m'    # Cyan
On_IWhite='\e[0;107m'   # White

А теперь мне нужно признаться: я обманщик. Такое объявление переменных не работает с многостраничным приглашением и вызывает неприятные «глюки». Я не знаю, почему это происходит и был бы рад, если бы вы рассказали мне об этом в комментариях. Ну а для того, что бы использовать цвета в приглашении командной строки, в описании этих escape последовательностей нужно добавить ещё один символ «[«. При этом в приглашении всё у вас будет хорошо, а вот при использовании этих переменных в при выводе в команде echo и другом подобном интерактиве, будет отображаться этот самый лишний символ. Итак, вот правильное содержимое файла с писанием цветов для использования в приглашении командной строки:

# Сброс
RstColor='\[\e[0m\]'       # Text Reset

# Обычные цвета
Black='\[\e[0;30m\]'        # Black
Red='\[\e[0;31m\]'          # Red
Green='\[\e[0;32m\]'        # Green
Yellow='\[\e[0;33m\]'       # Yellow
Blue='\[\e[0;34m\]'         # Blue
Purple='\[\e[0;35m\]'       # Purple
Cyan='\[\e[0;36m\]'         # Cyan
White='\[\e[0;37m\]'        # White

# Жирные
BBlack='\[\e[1;30m\]'       # Black
BRed='\[\e[1;31m\]'         # Red
BGreen='\[\e[1;32m\]'       # Green
BYellow='\[\e[1;33m\]'      # Yellow
BBlue='\[\e[1;34m\]'        # Blue
BPurple='\[\e[1;35m\]'      # Purple
BCyan='\[\e[1;36m\]'        # Cyan
BWhite='\[\e[1;37m\]'       # White

# Подчёркнутые
UBlack='\[\e[4;30m\]'       # Black
URed='\[\e[4;31m\]'         # Red
UGreen='\[\e[4;32m\]'       # Green
UYellow='\[\e[4;33m\]'      # Yellow
UBlue='\[\e[4;34m\]'        # Blue
UPurple='\[\e[4;35m\]'      # Purple
UCyan='\[\e[4;36m\]'        # Cyan
UWhite='\[\e[4;37m\]'       # White

# Фоновые
On_Black='\[\e[40m\]'       # Black
On_Red='\[\e[41m\]'         # Red
On_Green='\[\e[42m\]'       # Green
On_Yellow='\[\e[43m\]'      # Yellow
On_Blue='\[\e[44m\]'        # Blue
On_Purple='\[\e[45m\]'      # Purple
On_Cyan='\[\e[46m\]'        # Cyan
On_White='\[\e[47m\]'       # White

# Высоко Интенсивные
IBlack='\[\e[0;90m\]'       # Black
IRed='\[\e[0;91m\]'         # Red
IGreen='\[\e[0;92m\]'       # Green
IYellow='\[\e[0;93m\]'      # Yellow
IBlue='\[\e[0;94m\]'        # Blue
IPurple='\[\e[0;95m\]'      # Purple
ICyan='\[\e[0;96m\]'        # Cyan
IWhite='\[\e[0;97m\]'       # White

# Жирные Высоко Интенсивные
BIBlack='\[\e[1;90m\]'      # Black
BIRed='\[\e[1;91m\]'        # Red
BIGreen='\[\e[1;92m\]'      # Green
BIYellow='\[\e[1;93m\]'     # Yellow
BIBlue='\[\e[1;94m\]'       # Blue
BIPurple='\[\e[1;95m\]'     # Purple
BICyan='\[\e[1;96m\]'       # Cyan
BIWhite='\[\e[1;97m\]'      # White

# Высоко Интенсивные фоновые
On_IBlack='\[\e[0;100m\]'   # Black
On_IRed='\[\e[0;101m\]'     # Red
On_IGreen='\[\e[0;102m\]'   # Green
On_IYellow='\[\e[0;103m\]'  # Yellow
On_IBlue='\[\e[0;104m\]'    # Blue
On_IPurple='\[\e[0;105m\]'  # Purple
On_ICyan='\[\e[0;106m\]'    # Cyan
On_IWhite='\[\e[0;107m\]'   # White

Теперь нужно загрузить переменные из файла командой source ~/.bash_colors. Посмотреть, как будет выглядеть текст можно командой echo c опцией -e:

Проверка отображения цвета в консоле

Проверка отображения цвета в консоле

Наконец, к главному

С цветами теперь всё стало понятно. Осталось разобраться с макросами и линиями, которые у меня соединяют строки приглашения. Начну с линий. Это обычные символы unicode, задуманные для рисования таблиц. Как можно заметить, я всего лишь использую символы:┌ , ─ , ├ ,└ и > . Расположив их соответствующим образом, я получил рисунок, который вы уже видели:

«Макросами» я называю служебные сокращения для вывода имени хоста, пользователя, текущего времени, рабочего каталога и т.д. Правильное их название: прерывания приглашения. Итак, этих прерываний не так уж и много:

\a символ ASCII bell (07)
\d дата в формате «День недели. месяц. день» (Чт. февр. 18)
\D{format} тоже дата, но поддерживается передача шаблона в формате strftime(3). Если шаблон пустой, используется шаблон локали.
\e символ ASCII escape (033) — используется для экранирования escape последовательностей (например, тех самых цветов)
\h короткий вариант имени хоста (без домена)
\H полное имя хоста
\j количество заданий, запущенных в текущем терминале
\l ID терминала
\n перевод каретки (переход на новую линию)
\r возврат каретки (выводить строку поверх)
\s имя оболочки (shell), значение переменной $0
\t текущее время в формате ЧЧ:ММ:СС 24 часа
\T текущее время в формате ЧЧ:ММ:СС 12 часов
\@ текущее время в формате ЧЧ:ММ с указанием до/после полудня (зависит от локали)
\A текущее время в формате ЧЧ:ММ
\u имя пользователя
\v версия оболочки
\V больше информации о версии
\w текущий рабочий каталог целиком, домашний каталог заменяется тильдой (~)
\W последняя часть пути рабочего каталога, домашний каталог заменяется тильдой (~)
\! номер последней введённой команды в истории
\# номер команды в текущей сессии
\$ символ заменяется на «#», если uid=0 (т.е. работает root) и остаётся «$» в остальных случаях

И, разумеется, никто не запрещает использовать вывод любой команды в приглашении. В том числе и вызов самописной функции. Я, например, написал функцию для вывода load average. Причём функция не просто выводит значения la, но и раскрашивает цифры в зависимости от значения. При превышении лимита цифры становятся красными. Итак, предлагаю создать файлик ~/.functions и вставить в него следующее содержимое:

#Значение la, при котором цифры станут красными
let CRIT_LOAD=2

function load_average {
# Load average
set -- `cat /proc/loadavg`

five=$1
let int_five=`echo $five | cut -d '.' -f1`

ten=$2
let int_ten=`echo $ten | cut -d '.' -f1`

fifteen=$3
let int_fifteen=`echo $fifteen | cut -d '.' -f1`

#FIVE
if [ $int_five -gt $CRIT_LOAD ]; then
        echo -ne "\e[0;31m"
fi
echo -n " $five"
if [ $int_five -gt $CRIT_LOAD ]; then
        echo -ne "\e[0;33m"
fi
#TEN
if [ $int_ten -gt $CRIT_LOAD ]; then
        echo -ne "\e[0;31m"
fi
echo -n " $ten"
if [ $int_ten -gt $CRIT_LOAD ]; then
        echo -ne "\e[0;33m"
fi
echo -ne "\e[0;33m"
#FIFTEEN
if [ $int_fifteen -gt $CRIT_LOAD ]; then
        echo -ne "\e[0;31m"
fi
echo -n " $fifteen "
if [ $int_fifteen -gt $CRIT_LOAD ]; then
        echo -ne "\e[0;33m"
fi
}

Обратите внимание, в функции я использую «голые» escape последовательности цветов, а не переменные. Дело в том, что файлик с функциями у меня большой и путешествует со мной по серверам. А вот файлик с определением переменных я за собой не таскаю.

Итак, осталось собрать всё вместе. Самое время открыть для редактирования файл ~/.bashrc и в конец файла добавить подключение файла с описанием цветов и файл с функциями:

source ~/.bash_colors
source ~/.functions

И после этих строк, собственно, объявим переменную PS1. Например, как у меня:

PS1="$Cyan┌──$RstColor[ $Yellow\u$Green@$Cyan\h$RstColor ] [$Yellow $(load_average) $RstColor] $RstColor[ $Cyan\t$RstColor ]\n$RstColor$Cyan├── $Green\w\n$Cyan└>$RstColor"

Ну и как отмечалось ранее, можно добавить любые символы. Главное, что бы консольный шрифт их поддерживал:

Звёздочки в приглашении командной строки

Звёздочки в приглашении командной строки