Unix way: "Гарики", они
и в Linux'е "гарики".
Автор: Вадим Хохлов
Введение
Один из принципов Unix состоит в том, что программа должна выполнять только одну функцию, но очень хорошо. Программы также должны уметь взаимодействовать друг с другом[1]. Имея специальный "клей", можно объединять различные программы в более сложные конструкции. Манипулируя таким образом программами можно решать различные задачи. Один из таких примеров и рассматривается в статье. Надеюсь, она будет полезна программистам, начинающим изучать программирование для Unix.
Постановка задачи
На сайте http://nrsoft.chat.ru располагается программка, которая позволяет просматривать гарики Игоря Губермана. Программа работает под Windows, а мне хотелось иметь что-то подобное для Linux. Таким образом, задача ставится в следующем виде: необходимо разработать набор bash-скриптов для выбора различных интересных высказываний из какой-нибудь базы. Должна существовать возможность вставить выбранное высказывание, например, в качестве подписи в письмо или показать на экране (в консоле или в X Window)
Структуры данных
Высказывания будут хранится в обычных текстовых файлах [2]. Это позволит, во-первых, формировать их с помощью несложных скриптов из файлов, найденных в Internet, а, во-вторых, использовать стандартные утилиты для обработки текста: grep, tail, head.
Структура файла следующая:
Первая строка - автор высказываний или другая информация.
Вторая строка - число высказываний.
Третья строка - число строк в высказывании.
Остальные строки - высказывания.
Каждое высказывание начинается со строки, содержащей звездочку и его номер. Вот фрагмент файла с гариками:
Игорь Губерман
243
4
* 0
Держа самих себя на мушке,
в чем наша слава, честь и сила,
Мы держим подлых у кормушки,
А слабоумных у кормила.
* 1
Не на годы, а на времена
Оскудела моя сторона,
Своих лучших сортов семена
В мерзлоту раскидала страна.
Выборка фразы из файла
Алгоритм выборки произвольной фразы из файла состоит в следующем:
Прочитать из файла автора высказываний.
Определить количество высказываний в файле.
Определить количество строк в фразе.
Сгенерировать случайный номер высказывания.
Выбрать нужную фразу из файла.
Для извлечения первых трех строк из файла мы будем использовать комбинацию команд head и tail. Первая печатает несколько первых строк файла, а вторая - последних. По умолчанию печатается 10 строк, но можно указать необходимое количество. Например, следующий фрагмент выводит на экран автора высказываний (предполагается, что имя файла с фразами передается скрипту как первый параметр):
head -n 1 $1
Для сохранения результата команды в переменной используется командная подстановка: (обратные кавычки) и $(). Результат фрагмента скрипта команда заменяется на вывод команды. $(команда) является конструкцией, специфичной для bash. Например, для определения количества строк в фразе используется следующий фрагмент скрипта:
cstr=head -n 3 $1 | tail -n 1
Генерация случайных чисел в bash-скриптах выполняется с помощью встроенной переменной $RANDOM. (Убедитесь, что в системе активирован сервис random. Прим.ред.) При каждом обращении к ней генерируется случайное число в диапазоне от 0 до 32767. Если же нам необходим диапазон от 0 до N, то используется остаток от деления $RANDOM на N, который вычисляется с помощью оператора %. Для вычисления арифметических выражений в bash используется конструкция $[ выражение ]. Следовательно, для получения номера высказывания можно использовать следующую запись:
$[ $RANDOM % head -n 2 $1 | tail -n 1 ]
Выборка строк по шаблону из файла осуществляется с помощью семейства команд grep. Шаблон искомой строки задается с помощью регулярных выражений. Например, следующее выражение:
^\* 34$
соответствует строке
* 34
Символ '^' означает начало строки, '$' - конец. Символ '*' имеет специальное значение, поэтому его необходимо экранировать символом '\'. Символы начала и конца строки в данном случае используются, чтобы исключить строки вида:
aa * 32 bb
Обычно grep выводит строки, совпадающие с шаблоном. Если же мы хотим получить также несколько строк после найденной, то используется параметр -A . В нашем случае интерес представляет не строка с номер высказывания, а сама фраза. Для ее извлечения из результат работы grep используется уже известный подход с tail:
grep -A $cstr "^\* $[ $RANDOM % head -n 2 $1 | tail -n 1 ]$" $1 |\ tail -n $cstr
Символ обратной косой черты в конце первой строки используется для продолжения
логической строки на новой физической. Таким образом, полный текст скрипта bphrases
имеет следующий вид:
#!/bin/bash if [ $# -ge 1 ];then #кол-во строк в фразе cstr=head -n 3 $1 | tail -n 1 #фраза grep -A $cstr "^\* $[ $RANDOM % head -n 2 $1 | tail -n 1 ]$" $1 |\ tail -n $cstr #автор echo -e "\t" head -n 1 $1 fi
С помощью оператора if скрипт проверяет, что указан обрабатываемый файл.
Работа с несколькими базами фраз
Для работы с несколькими базами фраз можно написать дополнительный скрипт, который будет случайным образом выбирать файл с высказываниями и передавать его скрипту bphrases. Имена файлов баз перечислены в специальном файле, первая строка которого, содержит число этих файлов. Текст скрипта bphrasesx имеет следующий вид:
#!/bin/bash # печатает на стандартный выход произвольную фразу из одной или нескольких строк # и ее автора из произвольного файла # $1 - имя файла, содержащего список файлов с фразами if [ $# -ge 1 ];then #номер строки с именем файла с фразами. Добавляем 2, т.к. счет с 1 #и надо пропустить первую строку, с числом файлов nstr=$[ $RANDOM % head -n 1 $1 + 2] bphrases head -n $nstr $1 | tail -n 1 fi
Написанные скрипты можно использовать различным образом. Например, я настроил kmail для вставки различных фраз в качестве подписи в письма.
Вывод фраз в X Window
Теперь напишем скрипт для X Window, который будет показывать гарики Игоря Губермана и позволит их копировать в clipboard. Аналогом команды cat для X-ов является команда xmessage. Например, команда
xmessage "This is an example of using the xmessage"
выведет простое окно с сообщением (см. на Рис.1):
Рис.1. Окно команды xmessage
Эта команда очень гибкая и позволяет использовать различные настройки. Например, можно добавить несколько кнопок внизу окна. Следующий фрагмент:
xmessage -buttons end:2,more:3,clip:4
добавляет три кнопки - end, more, clip. Число после имени кнопки - значение, которое будет возвращать xmessage при нажатии на соответствующую кнопку. Нажатие на кнопку end будет завершать работу скрипта, more - показывать следующий гарик, clip - копировать текст текущего гарика в clipboard.
Для более тонкой настройки можно использовать класс окна. Этот параметр широко используется в X Window и оконными менеджерами. Например, следующая строка:
xmms.workspace: 2
заставляет IceWM запускать xmms на третьем рабочем столе. Большинство программ X Window позволяют задавать класс окна через параметр -name. Узнать класс окна и другие его характеристики можно с помощью команды xprop. Различные параметры приложений можно указывать в файле ~/.Xresources. Для того чтобы изменения, сделанные в этом файле вступили в силу, необходимо выполнить команду:
xrdb -merge $HOME/.Xresources
Задав класс окна gariki для xmessage, укажем шрифт, цвета и тексты на кнопках в ~/.Xresources:
gariki*font: -misc-fixed-*-*-*-*-20-*-*-*-*-*-*-* gariki*background: lightgreen gariki*end.label: Хватит gariki*end.background: red gariki*more.label: Еще\ хочу... gariki*more.background: magenta gariki*clip.label: Копировать gariki*clip.background: lightblue gariki*Text.background: yellow
С этими параметрами окно xmessage будет иметь вид, показанный на Рис.2.
Рис.2. Окно скрипта gariki
При завершении работы xmessage устанавливает код завершения, зависящий от того, какая кнопка была нажата. Переменная $? содержит код завершения последней команды. Она будет обрабатываться следующим образом:
ret=$? [ $ret -le 2 ] && exit 0
Строка [ $ret -le 2 ] && exit 0 является более компактным аналогом оператора if. Часть строки до && проверяет, что значение переменной ret не больше 2, а вторая часть выполнится только в том случае, если проверяемое условия истинно.
Работа с clipboard
Для работы с clipboard можно использовать утилиту xclip. Она позволяет показывать содержимое буфера обмена, помещать в него содержимое файла и выполнять ряд других функций. По умолчанию используется режим XA_PRIMARY, т.е. для вставки содержимого clipboard в кое-либо поле ввода достаточно нажать среднюю кнопку мыши[3].
Собираем все вместе
Таким образом, полный текст скрипта gariki имеет следующий вид [4]:
#!/bin/bash msg=bphrasesx ${1:-~/texts/lit/Gariki/files} while : do xmessage -name gariki -buttons end:2,more:3,clip:4 -default end \ -center -title "Гарики" "$msg" ret=$? [ $ret -le 2 ] && exit 0 [ $ret -eq 4 ] && echo "$msg" | xclip -i [ $ret -eq 3 ] && msg=bphrasesx ${1:-~/texts/lit/Gariki/files} done
Строка ${1:-~/texts/lit/Gariki/files} указывает, что необходимо взять значение переменной $1, а если она не определена, то ~/texts/lit/Gariki/files. Естественно, Вы должны использовать свое значение по умолчанию. Переменную msg необходимо брать в кавычки для правильной обработки символов перевода строки.
Для удобства я собрал все скрипты а также файлы с гариками и высказываниями Владимира Вишневского и Ежи Леца в одном архиве.
Заключение
Используя лишь стандартные "кирпичики", мы написали полнофункциональное приложение. При этом не пришлось прибегать к тяжелой артиллерии в виде C или C++.
Об авторе
Я работаю программистом и преподаю в Херсонском государственном техническом университете. С Linux знаком с 1999 года. Общаюсь с ним, в основном, дома. Кроме этого, я являюсь разработчиком IceWM Control Center - набора программ (в том числе и скриптов icerrun) для настройки различных параметров IceWM.
Мои хобби - игра в Что?Где?Когда?, аквариум, коты.
Ссылки
[1] Об этих принципах хорошо написано в [1], [2], [3].
[2] О преимуществах текстового представления информации см. в [2], [3].
[3] Правда, это не всегда работает с редакторами KDE.
[4] Не помню, кто сказал, что лучший способ надоесть - рассказать все до конца. Поэтому я намеренно не объяснял некоторые моменты подробно.
Рекомендуемая литература
Unix - универсальная среда программирования. К сожалению, я не помню ее координат.
Керниган Б., Пайк Р. Практика программирования. - СПб.: Невский диалект, 2001. - 381 с.
Соответствующие страницы man и info.
Различные руководства по программированию на bash.
Copyright (c) Septemper 2003, Vadim A. Khohlov