Красные глаза
Привет, представляю вашему вниманию небольшой туториал по оформлению приглашения командной строки с эффектом красных глаз.
Все кто работал в консоли какого-нибудь Линукса наверно отмечали удобную особенность отображать текущую папку, имя пользователя, имя сервера и что-то еще в зависимости от дистра в строке приглашения. Мне тоже это всегда нравилось. Но иногда получалось вот так:
Сложно «творить» в строке из трех символов, конечно курсор переходит на следующую строку, но это скорей еще больше бесит чем помогает. И вот в какой-то момент я сказал себе:
Я решил вернуть себе все пространство командной строки и больше не отдавать никому и никогда. Встал вопрос, как изменить текст приглашения командной строки? Оказалось, очень просто, достаточно изменить специальную системную переменную `PS1`.
Да, прямо в терминале можно задать новый текст приглашения. Но как сохранить изменения? Да и без информации о текущем каталоге как-то неуютно себя чувствуешь, постоянно задаешь себе вопрос: «Где я?» Поможет файл ~/.bashrc, в нём можно сохранить изменение PS1, а чтобы информация о текущей директории не занимала рабочее пространство, я решил разместить её не В, а НАД командной строкой. Добавим в файл ~/.bashrc строку:
PS1='$PWD\n# '
Обратите внимания на одинарные кавычки, если мы используем двойные, то вместо указателя на переменную $PWD (системная переменная, в которой храниться полный путь текущей папки, аналог команда pwd) в строку приглашения запишется ее значение (текущий каталог) и меняться при переходе из папки в папку не будет. Выглядит это так:
Командная строка полностью свободна, но имя папки сливается с содержимым если выполнить команду ls. Придется отделить мух от котлет имя папки от содержимого. Я решил добавить «рамки» для $PWD, добавив сверху и снизу строки из символов »-». Как узнать количество символов в строке? Для этого тоже есть системная переменная $COLUMNS. А чтобы быстро сформировать строку с необходимым количеством символов »-» воспользуемся командой printf:
printf -v line "%${COLUMNS}s"
Эта команда создаст переменную $line и заполнит её пробелами в количестве $COLUMNS, но нам нужны не пробелы, а »-», для этого используем следующий трюк:
line=${line// /-} # заменить все пробелы на -
Добавим этот код в ~/.bashrc
printf -v line "%${COLUMNS}s"
line=${line// /-}
PS1='\n$line\n$PWD\n$line\n# '
Отлично, но если сейчас изменить размер окна терминала, размер «линий» не изменится и красота пропадет:
Чтобы поправить ситуацию перенесем новый код в функцию info и добавим её в PS1:
info () {
printf -v line "%${COLUMNS}s"
line=${line// /-}
printf "\n$line\n$PWD\n$line\n# "
}
PS1='$(info)'
Можно совместить приятное с полезным, добавив в верхний ограничитель «вставку» с именем хоста. Вставка с именем будет посередине, для этого нам необходимо вычислить центр линии (с учетом длинны названия хоста и доп. символов):
info () {
name_lenght="{ $HOSTNAME }"
name_lenght=${#name_lenght}
top_line_left=$[(COLUMNS-name_lenght)/2]
top_line_right=$[COLUMNS-(top_line_left+name_lenght)]
printf -v top_line "%${top_line_left}s{_S_${HOSTNAME}_S_}%${top_line_right}s"
printf -v bot_line "%${COLUMNS}s"
bot_line=${bot_line// /-}
top_line=${top_line// /-}
top_line=${top_line//_S_/ }
printf "\n$top_line\n$PWD\n$bot_line\n# "
}
PS1='$(info)'
Я заключил имя хоста в фигурные скобки с пробелами, но вместо пробелов вокруг $HOSTNAME используются символы »_S_» которые затем меняются на пробелы. Это необходимо потому, что в итоговой строке все пробелы заменяются на »-», а внутри вставки должны остаться пробелы. Добавим красок, для этого подготовим переменные с кодами изменения цвета текста в терминале, я использовал такие цвета:
RED='\e[31m' # красный
GRN='\e[32m' # зелёный
YLW='\e[33m' # жёлтый
BLU='\e[34m' # синий
MGN='\e[35m' # пурпурный
DEF='\e[0m' # вернуть значения по умолчанию
BLD='\e[1m' # жирно
DIM='\e[2m' # тускло
Добавим эти переменные в наш код:
info () {
name_lenght="{ $HOSTNAME }"
name_lenght=${#name_lenght}
top_line_left=$[(COLUMNS-name_lenght)/2]
top_line_right=$[COLUMNS-(top_line_left+name_lenght)]
printf -v top_line "%${top_line_left}s{_S_$DEF$BLD$HOSTNAME${DEF}_S_$GRN}%${top_line_right}s"
printf -v bot_line "%${COLUMNS}s"
bot_line=$GRN${bot_line// /-}$DEF
top_line=${top_line// /-}
top_line=$GRN${top_line//_S_/ }$DEF
printf "\n$top_line\n$BLD$BLU$PWD$DEF\n$bot_line\n# "
}
PS1='$(info)'
Идем дальше, правая часть выглядит пусто, можно разместить там дату, и время:
printf -v date "%(%a %d %b %T)T"
Чтобы разместить это справа необходимо добавить какое-то количество пробелов после $PWD, какое, давайте посчитаем:
center_space=$[COLUMNS-${#date}-${#PWD}]
((center_space<0)) && center_space=1
...
printf "\n$top_line\n$BLD$BLU$PWD$DEF%${center_space}s$DIM$date\n$bot_line\n# "
Можно ли сделать лучше? Конечно, добавим вывод git status если находимся в папке с git проектом:
git_tst= git_clr=
[[ -d .git ]] && {
git_tst=(GIT $(git status -sb))
git_tst="${git_tst[*]} " # простой для теста
git_clr=(GIT $(git -c color.ui=always status -sb))
git_clr="${git_clr[*]} " # цветной для вывода
}
...
center_space=$[COLUMNS-${#date}-${#PWD}-${#git_tst}]
...
printf "\n$top_line\n$BLD$BLU$PWD$DEF%${center_space}s${git_clr[*]}$DIM$date\n$bot_line\n\$ "
Обратите внимания что git_clr и git_tst сначала записываются массивом, а затем преобразуются в переменные. Это необходимо для того чтобы удалить переходы строки из вывода git status. Но где же глаза? Сейчас будут глаза О_о, создадим массив с базовым набором глаз:
eyes=(O o ∘ ◦ ⍤ ⍥)
И посчитаем их количество:
en=${#eyes[@]}
Добавим символ рта:
mouth='_'
И сделаем генератор случайных морд:
face () {
printf "$YLW${eyes[$[RANDOM%en]]}$mouth${eyes[$[RANDOM%en]]}$DEF"
}
Глаза будут находиться по краям информационного поля, необходимо учесть их при расчете количества пробелов в середине, подготовим для этого отдельную переменную:
face_tst='O_o o_O'
...
center_space=$[COLUMNS-${#date}-${#PWD}-${#git_tst}-${#face_tst}]
printf "\n$top_line\n$(face) $BLD$BLU$PWD$DEF%${center_space}s$git_clr$DIM$date $(face)\n$bot_line\n\$ "
Как заставить глаза покраснеть? Долго сидеть за компом, не спать На stackoverflow.com мне попался интересный вопрос, автор спрашивает: «как менять цвет в приглашении командной строки на красный если последняя команда завершилась неудачно?» Это и натолкнуло меня на идею красных глаз. Добавим в функцию info запоминание статуса завершения последней команды:
info () {
error=$?
...
}
И изменим функцию face таким образом, чтобы она проверяла переменную $error и в зависимости от её значения красила глаза в красный или жёлтый:
face () {
[[ $error -gt 0 ]] && ecolor=$RED || ecolor=$YLW
printf "$ecolor${eyes[$[RANDOM%en]]}$YLW$mouth$ecolor${eyes[$[RANDOM%en]]}$DEF"
}
Ну вот у меня глаза покраснели, но можно добавить еще кое-что. Добавим проверку переменной $debian_chroot:
[[ $debian_chroot ]] && chrt="($debian_chroot)" || chrt=
...
name_lenght="{ $HOSTNAME$chrt }"
...
printf -v top_line "%${top_line_left}s{_S_$DEF$BLD$HOSTNAME$chrt${DEF}_S_$GRN}%${top_line_right}s"
И изменим текст в заголовке окна терминала:
PS1='$(info)'; case "$TERM" in xterm*|rxvt*) PS1="\[\e]0;$(face 1) \w\a\]$PS1";; esac
В заголовок будет выводится морда и текущий каталог, но придется немного доработать функцию face чтобы она рисовала морду без цветовых кодов, они в заголовке бутут отображаться просто текстом, передадим функции face какой-нибудь параметр (например »1»), внутри функции добавим проверку, если задан 1-й аргумент, выдать морду без разукрашивания:
face () {
[[ $error -gt 0 ]] && ecolor=$RED || ecolor=$YLW
[[ $1 ]] && printf "${eyes[$[RANDOM%en]]}$mouth${eyes[$[RANDOM%en]]}" \
|| printf "$ecolor${eyes[$[RANDOM%en]]}$YLW$mouth$ecolor${eyes[$[RANDOM%en]]}$DEF"
}
Итоговый скрипт:
RED='\e[31m' # красный
GRN='\e[32m' # зелёный
YLW='\e[33m' # жёлтый
BLU='\e[34m' # синий
MGN='\e[35m' # пурпурный
DEF='\e[0m' # вернуть значения по умолчанию
BLD='\e[1m' # жирно
DIM='\e[2m' # тускло
eyes=(O o ∘ ◦ ⍤ ⍥) en=${#eyes[@]} mouth='_'
face () {
[[ $error -gt 0 ]] && ecolor=$RED || ecolor=$YLW
[[ $1 ]] && printf "${eyes[$[RANDOM%en]]}$mouth${eyes[$[RANDOM%en]]}" \
|| printf "$ecolor${eyes[$[RANDOM%en]]}$YLW$mouth$ecolor${eyes[$[RANDOM%en]]}$DEF"
}
info () { error=$? git_tst= git_clr=
[[ -d .git ]] && {
git_tst=(GIT $(git status -sb))
git_tst="${git_tst[*]} " # простой для теста
git_clr=(GIT $(git -c color.ui=always status -sb))
git_clr="${git_clr[*]} " # цветной для вывода
}
[[ $debian_chroot ]] && chrt="($debian_chroot)" || chrt=
name_lenght="{ $HOSTNAME$chrt }"
name_lenght=${#name_lenght}
face_tst='O_o o_O'
top_line_left=$[(COLUMNS-name_lenght)/2]
top_line_right=$[COLUMNS-(top_line_left+name_lenght)]
printf -v top_line "%${top_line_left}s{_S_$DEF$BLD$HOSTNAME$chrt${DEF}_S_$GRN}%${top_line_right}s"
printf -v bot_line "%${COLUMNS}s"
printf -v date "%(%a %d %b %T)T"
top_line=${top_line// /-}
top_line=$GRN${top_line//_S_/ }$DEF
bot_line=$GRN${bot_line// /-}$DEF
center_space=$[COLUMNS-${#date}-${#PWD}-${#git_tst}-${#face_tst}]
((center_space<0)) && center_space=1
printf "\n$top_line\n$(face) $BLD$BLU$PWD$DEF%${center_space}s$git_clr$DIM$date $(face)\n$bot_line\n\$ "
}
PS1='$(info)'; case "$TERM" in xterm*|rxvt*) PS1="\[\e]0;$(face 1) \w\a\]$PS1";; esac
На этом все, спасибо за внимание!) Подписывайтесь, ставьте лайки, вот это вот все, проект есть в гитхабе info-bar Творите, выдумывайте, пробуйте!)
Вы заметили в какой момент символ »#» в строке приглашения поменялся на »$»?)