Unix way: "Гарики", они и в Linux'е "гарики".
Автор: Вадим Хохлов


Введение

Один из принципов Unix состоит в том, что программа должна выполнять только одну функцию, но очень хорошо. Программы также должны уметь взаимодействовать друг с другом[1]. Имея специальный "клей", можно объединять различные программы в более сложные конструкции. Манипулируя таким образом программами можно решать различные задачи. Один из таких примеров и рассматривается в статье. Надеюсь, она будет полезна программистам, начинающим изучать программирование для Unix.

Постановка задачи

На сайте http://nrsoft.chat.ru располагается программка, которая позволяет просматривать гарики Игоря Губермана. Программа работает под Windows, а мне хотелось иметь что-то подобное для Linux. Таким образом, задача ставится в следующем виде: необходимо разработать набор bash-скриптов для выбора различных интересных высказываний из какой-нибудь базы. Должна существовать возможность вставить выбранное высказывание, например, в качестве подписи в письмо или показать на экране (в консоле или в X Window)

Структуры данных

Высказывания будут хранится в обычных текстовых файлах [2]. Это позволит, во-первых, формировать их с помощью несложных скриптов из файлов, найденных в Internet, а, во-вторых, использовать стандартные утилиты для обработки текста: grep, tail, head.

Структура файла следующая:

Каждое высказывание начинается со строки, содержащей звездочку и его номер. Вот фрагмент файла с гариками:


Игорь Губерман
243
4
* 0
Держа самих себя на мушке,
в чем наша слава, честь и сила,
Мы держим подлых у кормушки,
А слабоумных у кормила.
* 1
Не на годы, а на времена
Оскудела моя сторона,
Своих лучших сортов семена
В мерзлоту раскидала страна.

Выборка фразы из файла

Алгоритм выборки произвольной фразы из файла состоит в следующем:

  1. Прочитать из файла автора высказываний.

  2. Определить количество высказываний в файле.

  3. Определить количество строк в фразе.

  4. Сгенерировать случайный номер высказывания.

  5. Выбрать нужную фразу из файла.

Для извлечения первых трех строк из файла мы будем использовать комбинацию команд 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] Не помню, кто сказал, что лучший способ надоесть - рассказать все до конца. Поэтому я намеренно не объяснял некоторые моменты подробно.

Рекомендуемая литература

  1. Unix - универсальная среда программирования. К сожалению, я не помню ее координат.

  2. Керниган Б., Пайк Р. Практика программирования. - СПб.: Невский диалект, 2001. - 381 с.

  3. The Art of Unix Programming.

  4. Соответствующие страницы man и info.

  5. Различные руководства по программированию на bash.


Copyright (c) Septemper 2003, Vadim A. Khohlov

Hosted by uCoz