Оформление вывода в shell-сценариях
В этой заметке будут обсуждаться:
- сюрпризы, которые может нам преподнести команда echo (читается как эхо);
- способы «расцвечивания» результатов работы наших сценариев;
- возможности управления перемещением курсора, очисткой экрана, раздвижкой и сдвижкой строк;
- использование и назначение программы tput и баз данных termcap и terminfo.
Коварное эхо
Однако, в 70-е годы прошлого века не все было так одназначно. Существовал один филосовский вопрос. Суть его была такова: что должна делать команда echo, если ей не передали аргументов, в частности, следует ли ей выдавать пустую строку или вообще ничего не предпринимать? По этому поводу велись большие дебаты. Такие, что вдохновили Дуга МакИлроя /Doug McIlroy/ сочинить притчу, которая приведена ниже.
UNIX и эхо.
Жила-была в стране Нью-Джерси, прекрасная девушка UNIX, к которой приезжали издалека, чтобы полюбоваться ею. Ослепленные чистотой UNIX, все искали ее руки и сердца: одни ≈ за изящество, другие ≈ за изысканную вежливость, третьи ≈ за проворность при выполнении самых изнурительных заданий. Была она от рождения столь великодушна и услужлива, что все женихи остались довольны ею, а ее многочисленное потомство распространилось во все концы земли.
Сама природа покровительствовала UNIX и вторила ей более охотно, чем кому-либо из смертных. Простые люди поражались ее эхом, таким оно было точным и кристально чистым. Они не могли поверить, что ей отвечают те же леса и скалы, которые так искажают их собственные голоса. Когда один нетерпеливый пастушок попросил UNIX: «Пусть эхо ответит ничего», и она послушно открыла рот, эхо промолчало. «Зачем ты открываешь рот? ≈ спросил пастушок. ≈ Отныне никогда не открывай его, если эхо должно ответить ничего!». ≈ и UNIX подчинилась.
«Но я хочу совершенного исполнения, даже если эхо отвечает ничего, ≈ потребовал другой, обидчивый, юноша, ≈ а никакого совершенного эха не получится при закрытом рте». Не желая обидеть никого из них, UNIX согласилась говорить разные «ничего» для нетерпеливого и обидчивого юношей. Она называла «ничего» для обидчивого как ‘\n’. Однако теперь, когда она говорила ‘\n’, на самом деле она не произносила ничего, поэтому ей приходилось открывать рот дважды: один раз, чтобы сказать ‘\n’, и второй раз, чтобы не сказать ничего. Это не понравилось обидчивому юноше, который тотчас сказал: «Для меня ‘\n’ звучит, как настоящее «ничего», но когда ты открываешь рот второй раз, то все портишь. Возьми второе «ничего» назад». Услужливая UNIX согласилась отказаться от некоторых эхо и обозначила это как ‘\с’. С тех пор обидчивый юноша мог услышать совершенное эхо «ничего», если он задавал ‘\n’ и ‘\с’ вместе, но говорят, что он так и не услышал его, поскольку умер от излишеств в обозначениях. [1]
Хотя все настоящие реализации этой команды выдают пустую строку, это еще не значит, что проблемы закончились. Давайте их рассмотрим. Рекомендую все, о чем я буду рассказывать, проверить на практике, сидя за консолью. Так будет легче разобраться.
Уже в 7-ой редакции UNIX echo распознавала флаг -n, который подавлял вывод завершающего символа перевода строки:
$ echo -n Seven Edition
Seven Edition$
В System V, где команда echo умеет интерпретировать упраляющие последовательности вида \Х, для подавления вывода завершающего символа перевода строки используют обозначение \с. Поэтому вызвав echo с аргументом -n вы можете получить не совсем то, что ожидали:
$ echo -n System V
-n System V
$
В Linux же, echo не желает обрабатывать последовательности вида \Х:
$ echo Linux\\с
Linux \с
$
до тех пор, пока вы не зададите ей флаг -e:
$ echo -e Linux\\с
Linux$
Можно воспользоваться и флагом -n, echo из Linux его понимает.
Добавляет путаницы и то, что почти всегда shell имеет встроенную команду echo, поведение которой может отличаться от внешней /bin/echo. Кроме того, в системе может оказаться не одна echo, а парочка. В таком случае, второе echo обычно находится где-нибудь в районе /usr/ucb/. Из-за таких различий в реализации echo возникают определенные трудности при переносе сценариев из системы в систему. Есть ли способ их избежать? Конечно, но это уже другая история.
Да будет цвет!
Перед тем как перейти к детальному рассмотрению esc-последовательностей, давайте вначале научимся вводить символ esc с клавиатуры (если вы умеете это делать, то можете переходить к выполению примеров из следующего абзаца). Чтобы ввести с клавиатуры код любой «непечатной», т. е. управляющей клавиши (функциональной, стрелки и т. п.), необходимо нажать Ctrl+V, а затем интересующую вас «кнопку». Этот прием будет работать не только в командной строке, но и в текстовом редакторе vi.
Проверим на практике. Выполним команду echo «Ctrl+V,Esc[30;40m»;clear (на экране символ esc будет показан как ^[):
$ echo «^[[30;40m»;clear
Если вы все сделали правильно, то приобретайте прибор ночного видения :)! Пожалуй я не ошибусь, предположив, что у вас возникли вопросы:
≈ А что это за числа? К чему они ведут? «А вдруг они не курят, а вдруг они не пьют?» [2]
≈ Числа 30 и 40 это аргументы команды esc[ n m. При необходимости указать несколько аргументов, их перечисляют через точку с запятой: esc[ n1 ; n2 ;. m. В зависимости от значения n результаты будут следующими: Теперича, имея в своем распоряжении такую табличку, можете приступать к получению «синих экранов», «красных квадратов» и прочей творческой работе. В которой желаю вам всяческих успехов (тем кто предпочитает другие слова ≈ удачи).
Что еще могут esc-последовательности?
Проведем испытания. Начнем с подготовки рабочего пространства. Выполните команду clear (или просто нажмите Ctrl+L). Затем ≈ более хитрую (напомню, что символы ^[ появляются в результате нажатия клавиш Ctrl+L,Esc):
termcap | terminfo | |
число символов в строке | co | cols |
число строк на экране | li | lines |
очистить экран | сl | clear |
включить повышенную яркость | md | bold |
включить мигание | mb | blink |
включить реверсное отображение | mr | rev |
установить цвет символов | AF | setaf |
установить цвет фона | AB | setab |
переместить курсор | cm | cup |
подать звуковой сигнал | bl | bel |
Более подробную информацию можно получить в манах по termcap и terminfo.
Таким образом, когда программе нужна информация о возможностях терминала, она определяет тип используемого терминала по значению переменной окружения TERM , затем обращается к базе данных и получает соответствующее типу терминала описание, которое и использует в дальнейшем для управления вводом/выводом.
Что же делать, если у вас возникло желание написать сценарий, который работал бы с любым, поддерживаемым операцинной системой, терминалом с одинаковой (или уменьшенной) функциональностью? Попробуйте воспользоваться утилитой tput. Синтаксис у нее следующий:
tput [-T тип_терминала] название_свойства [параметр(ы) . ]
В качестве значения аргумента название_свойства может быть задано любое имя из списка свойств базы данных termcap и/или terminfo для терминала указанного в тип_терминала. Если этот аргумент опущен, то tput будет использовать значение переменной TERM .
Для кодирования цветов используются следующие числа (в скобках указываются цвета для режима повышенной яркости):
Однако, не все так просто в нашем королевстве. Будьте готовы к тому, что в одних системах (BSD) tput будет ожидать название_свойства в стиле termcap, а в других (System V) в стиле terminfo. Примеры:
Всем, кто хочет изучить материальную часть более предметно, рекомендую поэкспериментировать с утилитами, которые конвертируют записи базы terminfo в termcap и наоборот. В BSD-системах это tconv, а в Linux и юниксах ветви AT&T infocmp. Вот вам еще примерчики:
Конечно же, существует много случаев, когда использование esc-последовательностей напрямую оправдано. Приведу в качестве примера фрагмент файла /etc/rc.status дистрибутива SuSE Linux (комментарии мои):
И небольшое замечание относительно ДОСа. Помните такую ОС? Так вот, если к ней прикрутить стандартный драйвер ANSI.SYS, то все вышеперечисленные esc-последовательности будут работать и там. 1. Брайан Керниган, Роб Пайк
UNIX универсальная среда программирования
М.: Финансы и статистика, 1992
2. Слова из песни, которые как известно не выбросишь.
Источник