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, который будет показывать гарики Игоря Губермана и позволит их копировать в 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 можно использовать утилиту 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] Не помню, кто сказал, что лучший способ надоесть - рассказать все до конца. Поэтому я намеренно не объяснял некоторые моменты подробно.
Copyright (c) Septemper 2003, Vadim A. Khohlov