- Подписка на печатную версию:
- Подписка на электронную версию:
- Подшивки старых номеров журнала (печатные версии)
LXF144:Bash
Материал из Linuxformat.
- Bash Создаем систему оповещений и диалоговые окна для скриптов
Содержание |
Bash: Графика и оболочке
Bash. Рэйчел Проберт |
---|
Bash. Ник Вейч |
---|
|
- Вся мощь системы кроется в командной строке, но некоторым людям нельзя доверить набор команд. Ник Вейч объединяет первое и второе.
Как известно, графический интерфейс помогает простым обывателям легко и удобно работать с компьютером. И, как известно, решать определенные компьютерные задачи в оболочке быстрее и безопаснее, а иногда это вообще единственный способ решения. Интерфейс нелегко запрограммировать с помощью скриптов, а вот в оболочке скрипты как дома. Но и тут найдется повод для создания интерфейса ваших прекрасных скриптов с человечеством: это допустит к скриптам других смертных или сделает скрипты чуть более автономными, не требующими особого вмешательства пользователя.
Оповещения о событиях
Первичное ваше пожелание – к примеру, известить кого-то, что скрипт или некая команда отработали. В современных рабочих столах такой механизм предусмотрен, и он называется оповещением о событиях [notification event]. Оповещения – это те маленькие окошки с сообщениями, что какая-то программа «упала», что вы подключили камеру или что ваш жесткий диск работает на пределе. В различных рабочих столах они обрабатываются по-своему (например, в KDE и Gnome обработчики разные), но, к счастью, freedesktop.org стандартизировал методы их работы, и обращаться к ним из командной строки можно одинаковым образом.
Еще более упрощает ситуацию то, что все эти действия можно выполнить с помощью простой команды: notify-send. Она принимает несколько параметров-ключей, определяющих длительность отображения сообщения, и пару строк – заголовок и, при необходимости, более подробное описание. Вот пример использования команды:
notify-send -t 1000 ‘Hello’ ‘World’
Конечно, в Bash мы можем сделать и похитрей – например, использовать переменные:
d=`date`;notify-send ‘The date is:’ “$d”
или отправлять сообщения по условию:
if [ “$snd_msg” -eq “1” ] then notify-send -t 2000 ‘yes’ ‘it is one’ fi
или изменять сообщение в зависимости от результата работы команды:
cp /tmp/bigfile /home/myname test $? = 0 && msg=’Большой файл скопирован’ || msg=’Копирование рухнуло’ notify-send -t 4000 ‘File Copy’ “$msg”
Прежде чем перейти к новому этапу, скажем пару слов о последних командах, которые некоторым читателям могут показаться необычными. Первая команда в данном случае просто копирует файл из одного места в другое. Bash формирует возвращаемое значение – 0 (ничего не произошло) или 1 (произошла ошибка). Вообще-то команда может вернуть сколько угодно значений, но 0 всегда означает, что она сделала то, что на нее возлагалось. Возвращаемое значение последней команды сохраняется в переменной окружения, доступ к которой мы получаем с помощью $?.
В следующей строке конструкцией test мы проверяем, являeтся ли условие (в данном случае, возвращаемое значение последней команды) истинным (поэтому за ним следует &&) или ложным (поэтому за ним следует ||). В результате мы выводим различные сообщения в зависимости от того, сработала или не сработала команда – вот вам маленький пример умений Bash. И этот совет достался вам даром! Ведь статья-то про графический интерфейс...
Установка напоминаний
Использование Bash вместе с системой оповещения может быть весьма плодотворным. Например, пусть каждые пять минут вам выдают сведения об использовании диска:
while true; do msg=`df`; notify-send “disk usage” “$msg”; sleep 120 ; done
или даже держат вас в курсе свежих сообщений ядра:
sudo tail -n0 -f /var/log/messages | while read msg; do notifysend “Новая запись в журнале” “$msg”; done
С виду кажется, что особого удобства здесь нет, но при помощи ряда параметров можно сделать команду notify-send более удобной и даже более интерактивной. Изменим наш исходный скрипт:
#!/usr/bin/env bash winicon=/usr/share/icons/gnome/32x32/status/dialoginformation.png failicon=/usr/share/icons/gnome/32x32/status/dialogwarning.png dest=$2 cp $1 $dest if [ $? -eq 0 ] then msg=”Ваш файл скопирован и находится в \n<a href=’$dest’>$dest</a>” icon=$winicon else msg=”Что-то поломалось, но я тут ни при чем” icon=$failicon fi notify-send -t 5000 -i $icon ‘File Copy’ “$msg”
Не трудитесь набирать скрипт целиком: он имеется на нашем DVD. Не то что мы считаем вас лентяями – просто Bash невероятно возбудим насчет кавычек, а на бумаге сложновато заметить раз-ницу между прямой ' и непрямой ‘ кавычками. В Bash почти всегда употребляются прямые кавычки.
Мы внесли два изменения в последний скрипт и изменили проверку. Переменные в начале содержат пути к файлам изображений – здесь они почерпнуты из библиотеки изображений Gnome, но вы можете взять и свои картинки. Затем они передаются команде notify-send, чтобы в сообщении появилась иконка – этак будет круче!
С помощью специальных переменных Bash $1 и $2 мы получаем первый и второй аргументы скрипта и используем их как источник и место назначения для команды copy, и опять же можем проверить ее результат с помощью $?.
Мы вернулись к конструкции if; then; fi, потому что по ней проще понять, что происходит, и не нужна огромная строка, которую долго набирать. По сути логика осталась прежней – в зависимости от успеха или неудачи отработки команды отображаются разные иконки и сообщения.
Само сообщение на сей раз содержит некий HTML-код. Он создает работающую ссылку на каталог, куда был скопирован файл, и с этой ссылкой оповещения становятся еще полезнее. Впрочем, в оповещениях будут работать не все HTML-тэги (в конце концов, это просто незатейливая система сообщений, без претензий), и если сообщение оказывается пустым, возможно, вы включили в него какие-то тэги, которые не были обработаны или которыми нельзя пользоваться.
Если запустить Dialog в командной строке без аргументов, он выведет большой список диалоговых окон, которые можно создать, вместе с необхо димыми параметрами.
Недостатки системы оповещения как средства взаимодействия пользователей очевидны. Хотя с ее помощью легко отправить сообщение пользователю рабочего стола, возможности отправки ответа выполняющейся программе ограничены.
Благодаря лежащей в основе Unix идее создания отдельных утилит для решения узких задач и их объединения для решения задач более сложных, в Linux можно творить чудеса. К сожалению, большинство людей этого не осознают – прежде всего потому, что боятся командных строк.
Пользовательский ввод в скриптах отнюдь не нов. В Bash это делается совсем просто:
read -p “Правда, здорово?” -e input echo Согласен, здорово $input
Проблема здесь в том, что все еще не обойтись без командной строки. Традиционный способ решения этой проблемы – Ncurses. Изначально Curses представляла собой библиотеку, разработанную энтузиастами из BSD для игры на терминалах, но идея ушла гораздо дальше. В Linux используются библиотеки Ncurses, разработанные в середине 1990‑х и знакомые каждому, кто пытался устанавливать BSD или Linux примерно до 2001 года. Каким-то хитрым образом она создает в терминале текстовый экран с несколькими точками фокуса, перемещаться между которыми можно клавишей Tab. Эти точки могут представлять собой практически любые элементы стандартного графического окна: например, галочки или текстовые поля.
Давайте попробуем одну такую.
src=$1 dest=`dialog --fselect ~ 15 25 3>&2 2>&1 1>&3` cp $src $dest test $? = 0 && msg=’Файл скопирован’ || msg=’Файл рухнул’ dialog --msgbox “$msg” 6 20
Большую часть скрипта мы уже рассмотрели прежде, но во второй строке есть некая хитрость. Переменная назначения устанавливается с помощью выражения в обратных апострофах – это значит, что Bash выполнит команду внутри апострофов (в английской раскладке клавиатуры такой апостроф обычно находится над клавишей Tab) и заменит выражение выводом этой команды. Понять это было бы легче, не будь в команде осложнений.
Прежде всего, знайте, что dialog – это команда. За ней всегда следует ключ, обозначающий тип диалогового окна и параметры окна. В данном случае это диалог выбора файла, который принимает исходный каталог (причем Bash заменяет ~ на ваш домашний каталог), ширину и высоту окна. А что за абракадабра идет дальше? От большого ума авторы решили отправлять вывод в поток STDERR вместо STDOUT, то есть вам не удастся его увидеть. Множество скриптов, использующих Dialog, перенаправляют вывод в файл и затем его выводят. Этот фрагмент использует стандартные перенаправления, чтобы поменять местами STDOUT (канал 1) и STDERR (канал 2) через дополнительный канал (3). Все, что он на самом деле делает – получает значение из диалогового окна, созданного командой dialog, и присваивает его переменной.
Остальная часть скрипта должна быть очевидной. В конце мы вызываем dialog снова, на этот раз с простым окошком сообщения, чтобы показать результат операции.
Использование Xdialog
Существует множество других типов диалоговых окон, но прежде чем нас увлекут симпатичные, но не вполне современные Ncurses, самое время рассказать об Xdialog. Теперь читайте внимательно, потому что следующее предложение – ложь. Xdialog – удобная замена Dialog, в которой вместо жалкой имитации используются настоящие окна GTK.
Заметили ложь? Xdialog – замена только почти. Синтаксис по большей части идентичен. В Xdialog есть пара лишних опций для выбора шрифтов и прорисовки диалога. В целях совместимости Dialog принимает множество этих опций и просто игнорирует их, поэтому по крайней мере для простых скриптов можно пользоваться и тем, и другим, существенно не изменяя код. (Совет: заведите в начале скрипта переменную, которая будет содержать фактически используемую команду.)
И наш код теперь выглядит так:
Xdialog больше не поставляется по умолчанию во многих дистрибутивах, и если вы захотите с ним поэкспериментировать, может потребоваться установка пакета Xdialog.
src=$1 dest=`Xdialog --fselect ~ 15 25 3>&2 2>&1 1>&3` cp $src $dest test $? = 0 && msg=’Файл скопирован’ || msg=’Файл рухнул’ Xdialog --msgbox “$msg” 6 20
Как видите, Xdialog остается совместимым, и для возвращения результата скрипту ему нужно проделывать те же операции с каналами. Поэтому, хотя с ним мы избавились от старомодных окошек Ncurses, он все еще довольно неудобен с точки зрения написания скрипта. Поэтому не будем тратить на него время и займемся Zenity.
Операция Zenity
Хотя Xdialog и переносит нас в XXI век с точки зрения стильности, с ним все еще не обойтись без затрат немалых усилий на написание скрипта. Манипуляция с каналами тоже будет работать не всегда, потому что иногда вам нужны значения из STDOUT вместо STDERR, а иногда – из обоих потоков.
Zenity несовместим с Dialog или Xdialog, хотя в общем-то похож на них. Кое в чем он упрощен и не делает все в точности так же, как Xdialog, но предоставляет симпатичный интерфейс GTK, и им будет сравнительно нетрудно воспользоваться в скриптах.
Напишем простой скрипт для утилиты FFmpeg и сконвертируем с ее помощью файл, приняв от пользователя ряд настроек.
Первое, что мы сделаем в этом случае – проверим, был ли запущен скрипт с именем файла в качестве параметра, а если нет – заставим пользователя выбрать файл.
[ “$#” -eq 1 ] && src=$1 \ || src=$(zenity --title “Выберите файл” --file-selection)
Здесь проверяется количество переданных параметров (в квадратных скобках), и либо первый параметр записывается в переменную src, либо мы получаем путь к файлу от Zenity.
Помещение выражения в $(…) в Bash аналогично обратным апострофам. Содержимое скобок выполняется как команды, и их результат подставляется на их место.
Синтаксис очень прост – Zenity не старается выглядеть сложнее, чем он есть. У него есть несколько опций, которые можно указать, но значения по умолчанию прекрасно подходят для большинства ситуаций, и вам не нужно возиться с перенаправлениями каналов.
Теперь посмотрим на список:
outsize=$(zenity --list --text “ Задайте выходное разрешение” \ --height 280 --radiolist \ --column “Pick one “ --column “size” --column “format” \ FALSE 352x288 CIF \ FALSE 320x240 QVGA \ FALSE 640x480 VGA \ FALSE 800x600 SVGA \ TRUE 1024x768 XGA \ FALSE 1280x720 HD720 \ )
На этот раз мы передали параметр list, чтобы создать список значений для выбора. Так как мы указали radiolist, в первом столбце должно быть значение TRUE для радиокнопки по умолчанию и FALSE для остальных. Есть еще два столбца: в одном помещаются сами разрешения, в другом – прочая информация; в данном случае – аббревиатура формата. Затем следуют собственно значения. Фразы из нескольких слов, если таковые имеются, необходимо взять в кавычки; удобно также воспользоваться переносами строк, чтобы каждый элемент списка находился на новой строке.
Значение из второго столбца, которое предупредительно соответствует формату, ожидаемому FFmpeg, передается обратно в нашу переменную. При необходимости опций можно добавить, и вот лишь небольшой пример того, что можно сделать:
[ “$#” -eq 1 ] && src=$1 \ || src=$(zenity --title “Выберите файл” --file-selection) echo $src outsize=$(zenity --list --text “Задайте выходное разрешение” \ --height 260 --radiolist \ --column “Pick one “ --column “size” --column “format” \ FALSE 640x480 VGA \ FALSE 800x600 SVGA \ TRUE 1024x768 XGA \ FALSE 1280x720 HD720 \ ) bit=$(zenity --scale --text “Max битрейт?” --min-value=50 --max-value=64000 --value=200 –step 1) dest=$(zenity --title “Укажите, где сохранить” --save –file-selection) ffmpeg -i $src -b $bit -s $outsize $dest 2>&1 | zenity --progress --pulsate
Опция scale создает «ползунок» с понятными параметрами, задающими пределы значений, и мы можем воспользоваться другим окошком выбора файлов, на этот раз с параметром –save, указав, что нам нужно новое имя файла.
Наконец, когда в конце скрипта мы запускаем саму команду ffmpeg, мы можем поменять местами каналы и направить результат в индикатор прогресса.
Идем дальше
Мы расширим этот проект, улучшив работу индикатора прогресса так, чтобы он отражал текущее состояние. Утилита FFmpeg не возвращает текущий прогресс в процентах, но может сообщить вам, который кадр перекодирует в данный момент. Если определить число кадров в исходном файле, можно определить прогресс, добыв текущий кадр (полезная подсказка: примените sed) и выполнив несложный расчет.
Итак, хотя мы не можем утверждать, что породнить командную строку и рабочий стол просто, это определенно полезно и весело. Если у вас есть интересные примеры, связанные с оболочкой или графическим интерфейсом, расскажите нам о них или пришлите свои предложения на форумы Linux Format по адресу http://www.linuxformat.ru.