Наши партнеры








Книги по Linux (с отзывами читателей)

Библиотека сайта rus-linux.net

Оформление вывода в shell-сценариях


Big Shadow
Киев, 2003

В этой заметке будут обсуждаться:

  • сюрпризы, которые может нам преподнести команда echo (читается как эхо);
  • способы «расцвечивания» результатов работы наших сценариев;
  • возможности управления перемещением курсора, очисткой экрана, раздвижкой и сдвижкой строк;
  • использование и назначение программы tput и баз данных termcap и terminfo.

Коварное эхо

Как вы знаете, команда echo помещает в стандартный вывод свои аргументы, разделенные пробелами, и завершаемые символом перевода строки.

Однако, в 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 возникают определенные трудности при переносе сценариев из системы в систему. Есть ли способ их избежать? Конечно, но это уже другая история...

Да будет цвет!

Каким образом происходит задание цвета символов и фона, а также переключение различных режимов отображения терминала в UNIX (и во множестве других ОС)? Да также, как и в легендарные времена телетайпных терминалов ≈ посылкой на устройство отображения управляющих команд в виде esc-последовательностей (esc сокр. от англ. escape, произносится как эскейп). Такая последовательность состоит из двух или более символов, первый из которых будет esc (десятичный код равен 27, восьмеричный ≈ 033, шестнадцатеричный ≈ 0x1B; во многих программах и конфигурационных файлах обозначается как \e, \E или ^[).

Перед тем как перейти к детальному рассмотрению 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 результаты будут следующими:

==================================================================
k | n=k - установка      | n=k+30 - установка      | цвет символов
  |       режима         |          цвета символов | в режиме
  |       отображения    | n=k+40 - установка      | повышенной
  |       символов       |          цвета фона     | яркости
--|----------------------|-------------------------|--------------
0 | сброс всех атрибутов | черный                  | серый
1 | яркий (утолщенный)   | красный                 | розовый
2 | тусклый              | зеленый                 | салатовый
3 |                      | коричневый              | желтый
4 | подчеркнутый         | синий                   | светло-синий
5 | мигающий             | фиолетовый              | лиловый
6 |                      | бирюзовый               | голубой
7 | реверсный            | белый                   | ярко-белый
==================================================================
Теперича, имея в своем распоряжении такую табличку, можете приступать к получению "синих экранов", "красных квадратов" и прочей творческой работе. В которой желаю вам всяческих успехов (тем кто предпочитает другие слова ≈ удачи)!!!

Что еще могут esc-последовательности?

Управлять перемещением курсора по экрану:
esc[s или
esc7
запомнить положение курсора
esc[u или
esc8
восстановить запомненное положение курсора
escc
очистить экран и установить курсор в левый верхний угол
esc[ n A
вверх на n строк
esc[ n B или
esc[ n e
вниз на n строк
esc[ n C или
esc[ n a
вправо на n позиций
esc[ n D
влево на n позиций
esc[ n E
в начало строки и на n строк вниз
esc[ n F
в начало строки и на n строк вверх
esc[ n1;n2 H или
esc[ n1;n2 f
переместить в позицию n2 строки n1
esc[ n Z
на n табуляций назад (как tab, но в обратную сторону)
esc[ n `
в той же строке в позицию n
esc[ n d
в той же позиции в строку n
escM
сдвинуть курсор на строчку вверх, если он находится в самой верхней строке, то сдвинуть содержимое экрана на строчку вниз

Очищать части экрана:
esc[0J
от курсора до конца экрана
esc[1J
от начала экрана до курсора
esc[2J
весь экран
esc[0K
от курсора до конца строки
esc[1K
от начала строки до курсора
esc[2K
всю строку
esc[ n X
очистить n знаков справа от позиции курсора

Раздвигать и сдвигать строки на экране:
esc[ n L
вставить n пустых строк ниже текущей строки
esc[ n M
удалить n строк ниже текущей строки
esc[ n P
удалить n знаков справа от курсора (в пределах строки)
esc[ n @
вставить n знаков справа от курсора (в пределах строки)

Проведем испытания. Начнем с подготовки рабочего пространства. Выполните команду clear (или просто нажмите Ctrl+L). Затем ≈ более хитрую (напомню, что символы ^[ появляются в результате нажатия клавиш Ctrl+L,Esc):

 $ echo '^[[10;10Habc^[[5dXYZ'

     HаЁршт                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         aw" && stty size > /dev/null 2>&1 ; then
# если наш терминал понимает, что такое ширина и высота,
# то назначаем кодам esc-последовательностей понятные названия
         esc=`echo -en "\033"`
        extd="${esc}[1m"
        warn="${esc}[1;31m"
        done="${esc}[1;32m"
        attn="${esc}[1;33m"
        norm=`echo -en "${esc}[m\017"`
        stat=`echo -en "\015${esc}[${COLUMNS}C${esc}[10D"`
        # ... неинтересное пропускаем
  rc_done_up="${esc}[1A${rc_done}"
rc_failed_up="${esc}[1A${rc_failed}"
    rc_reset="${norm}"
     rc_save="${esc}7"
  rc_restore="${esc}8"
    function rc_cuu () { echo -en "\033[${1}A"; }
    # и даже определяем функцию
    # для перемещения по экрану вверх на заданное число строк
else
# ну а если терминал не знает где у него верх, а где низ,
# то ведем себя проще и выдаем информацию построчно без выкрутасов,
# чтобы люди к нам тянулись :) ...

И небольшое замечание относительно ДОСа. Помните такую ОС? Так вот, если к ней прикрутить стандартный драйвер ANSI.SYS, то все вышеперечисленные esc-последовательности будут работать и там.


1. Брайан Керниган, Роб Пайк
UNIX ≈ универсальная среда программирования
М.: Финансы и статистика, 1992

2. Слова из песни, которые как известно не выбросишь...