Документация по ОС FreeBSD Суббота, 27.04.2024, 00:01
Приветствую Вас Гость | RSS
Меню сайта

Категории каталога
Shell [40]

Главная » Статьи » Программирование » Shell

Шелл для кодера - Программируем на bash [2005]

Программируем на BASH. Разбор реального сценария

Можно сколько угодно спорить о том, какой язык программирования лучше, но нельзя спорить только с тем, что необходимо использовать то, что позволит тебе добиться достойного результата при наименьших затратах времени и сил. Программирование на скриптовых языках, с одной стороны, является достаточно простым и понятным, а с другой - достаточно гибким и мощным средством для решения многих повседневных задач.

Языки сценариев полезны для пользователя и просто жизненно необходимы для любого системного администратора. *nix-системы имеют множество встроенных и прикладных языков программирования. Наиболее популярными и часто используемыми из них являются Perl, Tcl, а также shell. Сейчас ты, наверное, подумал: "Ха, да ведь шелл – это же командная оболочка, являющаяся как бы посредником между человеком и системой для упрощения взаимодействия". Совершенно верно, но не только! Это еще и мощное средство программирования. Плюсы использования интерпретируемых языков программирования очевидны. Вот только некоторые из них.

1. Переносимость: ты можешь легко залить свой только что испеченный скрипт с машины, на которой установлена твоя любимая Fedore Core, на любую другую платформу под управлением скажем FreeBsd или Solaris. Главное, чтобы в системе был установлен интерпретатор для языка, на котором написан крипт.

2. Простота написания кода: нет необходимости специально обучаться сложному программированию на компилирующих языках, таких как С, С++, Pascal, Fortran. Такое программирование наиболее очевидно, поскольку программа пишется в пошаговом режиме, то есть человек принимает решение о своем следующем шаге в зависимости от реакции системы на предыдущий шаг.

3. Быстрота написания кода: благодаря простоте синтаксиса и отладки ты сэкономишь много времени.

4. Большие функциональные возможности: хотя интерпретируемые языки и нельзя сравнить по своей функциональности, например, с С, тем не менее не нужно недооценивать всей их мощи.

Программирование на shell

Давай рассмотрим программирование на shell более подробно. Поскольку у тебя на Linux определенно есть sh и, скорее всего, bash, то нет никакой необходимости устанавливать пакеты этих программ, а можно сразу же приступить к программированию. Сразу же оговорюсь: в своих экспериментах я использовал интерпретатор bash.

Нет ничего страшного, если в качестве оболочки ты используешь sh. Возможно также, что у тебя установлен Korn Shell (ksh) или что-то еще, тогда тебе нужно всего лишь придерживаться стандарта POSIX, если хочешь, чтобы твои shell-сценарии могли быть интерпретированы другим шеллом. Пройдя по ссылке www.unix.org.ua/orelly/unix/ksh/appa_02.htm, ты сможешь прочитать статью о IEEE 1003.2 POSIX shell стандарте и его поддержке в Korn shell. Дополнительные же преимущества bash опишу чуть позже.

Важно понять, что из сценариев доступны абсолютно все команды и утилиты системы, а внутренние команды shell - условные операторы, операторы циклов и др. только увеличивают мощь и гибкость сценариев.

Обычно все сценарии начинаются с одной из следующих строк или набора строк:

#!/bin/sh
#!/bin/bash
#!/usr/bin/perl
#!/usr/bin/tcl
#!/bin/sed -f
#!/usr/awk –f

Ты, наверное, уже заметил, что каждая строка начинается одинаково, с символов «#!». Эти строки объясняют системе, что запущенный файл, - это не что иное, как сценарий, и его следует обработать с помощью указанного после символов "#!" интерпретатора.

Запустить сценарий можно двумя способами. Первый заключается в предоставлении права на исполнение файла для владельца файла, группы или всех пользователей в системе. Полный доступ к файлу на выполнение, запись и чтение для владельца выглядит так:

сhmod 700 my_script_name.sh

или

chmod u+rwx my_script_name.sh

Доступ для всех остальных, я думаю, ты сможешь сделать сам. Второй способ заключается в указании интерпретатора перед именем сценария:

sh my_script_name
bash my_script_name

От теории к практике

Я думаю, наступил момент рассмотреть какой-нибудь пример практического использования скрипта, из которого сразу все станет ясно. Но перед этим хочу обратить твое внимание на то, что некоторую полезную роль в написании кода играют редакторы. Современные редакторы имеют уйму разных функций от подсветки кода до вставки в текст некоторых несложных конструкций. Все это на любителя. Ты можешь использовать vi или emacs, а мне хватает встроенного в Midnight comander редактора с подсветкой синтаксиса. Ну а выбор, как всегда, за тобой. Определись, что тебе больше нравится, и забудь.

Чтобы далеко за примерами не ходить, я решил залогиниться на свой ALT Master 2.2 и взять первый попавшийся на глаза скрипт запуска одного из демонов. Такие скрипты, как известно, находятся в /etc/rc.d/init.d/. Первым в каталоге init.d оказался файл Anacron – сценарий запуска для одного очень популярного планировщика задач, похожего на всем известный демон cron. Anacron также может периодически запускать команды в назначенное время, и в отличие от cron, нет необходимости постоянной работы системы (но это уже совсем другая история).

Разберем файл построчно. Как мы видим, в начале файла используется до боли знакомая конструкция, начинающаяся с «#!». Ты уже знаешь, на что она указывает системе. Далее следуют комментарии, которые предваряются символом «#». А вот тут уже становится интересно: появилась какая-то строка, да еще с точкой в начале. Вот она:

. /etc/init.d/functions

На самом деле ничего странного здесь нет. Символ "." является эквивалентом команды source. Внутри сценария команда source other_file_name подключает файл other_file_name. Она очень напоминает директиву препроцессора языка C/C++ - "#include". Коротко пробегись по включенному файлу и получи представление о том, что же представляет собой скрипт functions. На самом деле все становится предельно ясно с комментариями к файлу. Этот сценарий содержит функции, наиболее часто используемые скриптами автозапуска из /etc/init.d. Дальше в файле как раз встречаются функции, которые очень часто можно найти в скриптах.

Но вернемся к нашему сценарию автозапуска anacron.

[ -f /usr/sbin/anacron ] || exit

В данной строке мы видим опять же эквивалент команды Test. Как понятно из названия, она проверяет условие, которое в этом случае заключено в квадратные скобки. Ключ –f задается для проверки существования файла. Таким образом, данный блок операторов служит для того, чтобы точно знать, существует ли файл демона /usr/sbin/anacron, и только в этом случае продолжать выполнение скрипта, а иначе выйти вон.

Далее следует инициализация переменных LOCKFILE и RETVAL, которая происходит при присвоении им определенных значений. Пока для нас эти переменные ничего не значат. При программировании на shell переменные не имеют типа, но в зависимости от того, какое значение им присвоено, возможна, например, целочисленная арифметика с переменными. После того как переменной присвоено значение, ее можно использовать в качестве подстановки, приписав в начале ее имени символ "$". И помни разницу между именем переменной (RETVAL) и ее значением ($RETVAL): если, например, посмотреть в самый конец рассматриваемого скрипта, обнаружишь строку exit $RETVAL. Здесь используется оператор exit для завершения программы, который тоже возвращает значение переменной RETVAL.

Ну вот мы и добрались до начинки файла - объявления функций start(), stop() и restart(). Под их контроль как раз и попадает обработка параметров, поступающих скрипту от пользователя или других программ. Как понятно из названий, каждая функция производит соответственно запуск, останов или restart демона. В принципе, здесь все понятно. Интересно то, что дальше в функциях встречаются не совсем логичные переменные $?, $$, $PPID. Ничего подобного не объявлялось, тогда откуда они взялись? Сейчас все станет ясно. Дело в том, что существует специальный тип переменных – так называемые переменные окружения. В рамках любого процесса есть некоторое окружение, то есть набор переменных, к которым он может обращаться за получением определенных данных. Каждый раз, когда запускается командный интерпретатор, для него создаются переменные окружения. Эти переменные можно экспортировать любому дочернему процессу с помощью команды Export. Список переменных можно получить командой Set. Количество переменных окружения достаточно велико, поэтому в командной строке лучше дать команду set|more для того, чтобы иметь возможность пролистать весь список.

Так вот, переменная $? содержит значение последней выполнившейся команды. А переменная $$ таит в себе не что иное, как PID сценария, то есть идентификатор процесса сценария. Переменная $PPID - PPID, то есть родительский идентификатор процесса.

Получается вот что (сразу не скажу, что): внимательно посмотри на функцию Start().

daemon anacron –s

Командой Daemon пытаемся запустить файл демона anacron с опцией –s для синхронизации заданий. При удачном запуске команда Daemon вернет значение "0".

Уже известно, что переменная $? будет содержать код возврата последней операции. Это значение и присваиваешь переменной RETVAL.

[ $RETVAL -eq 0 ] && touch "$LOCKFILE"

И теперь просто проверяешь, запущен ли демон, и только в этом случае создашь файл, содержащийся в переменной LOCKFILE, который необходим для работы демона.

С функцией stop() все абсолютно то же самое. Проверяешь, действительно ли запущен демон, и если все благополучно, вызываешь команду Killproc, которая убивает демона. И подчищаешь созданные ранее файлы.

Функция restart() поочередно вызывает функции start() и stop(), чтобы перезапустить демон.

Ниже в файле расположен оператор выбора case, который выполняет тот или иной участок кода согласно заданным условиям. Иногда case называют блоком операторов, поскольку его можно представить в качестве большого количества операторов проверки условия if, then, else. В нашем случае проверяется значение переменной $1, которое представляет собой не что иное, как первый параметр, передающийся скрипту. Например, если в командной строке набрать anacron start, выполнится условие start, которое вызовет функцию start() и запустит демон. Блок case завершает ключевое слово esac.

Отладка shell-сценариев

Нужно признаться, что до выхода последней версии (bash 3.0) командный интерпретатор bash не имел своего отладчика и даже каких-либо отладочных команд, возможно, за исключением команды Trap, которая устанавливает ловушки на сигналы, то есть определяет, какие действия нужно выполнить при получении сигнала. Формат команды Trap следующий:

trap [-lp] [arg] [sigspec ...]

Команда Arg выполняется при получении командным интерпретатором указанных сигналов sigspec. Если указана опция -p, выдаются команды Trap, связанные с каждым из перечисленных сигналов. Опция -l приводит к выдаче списка имен сигналов и соответствующих им номеров. Сигнал можно задавать как по имени, определенному в файле <signal.h>, так и по номеру. Если в качестве сигнала указано DEBUG, команда Arg выполняется после каждой простой команды. Trap возвращает 0 в случае успеха, в противном случае -1.

Могут пригодиться некоторые команды, которые, казалось бы, к отладке не имеют никакого отношения. Например - команда Echo, которая умеет выводить значения переменных в процессе выполнения скрипта.

А если хочешь чего-то более продвинутого, то в целях отладки можешь воспользоваться командой Tee и функцией assert(). Tee проверяет процессы и потоки данных в опасных ситуациях, а функция assert() служит для проверки переменных и условий в указанных точках сценария.

Конечно же, ты помнишь, что скрипт можно запустить строкой вида:

bash my_script_name

Если интерпретатору передать аргументы –n, -v или -x перед именем сценария, можно еще и получить некоторую полезную информацию. Ключ –n проверяет наличие синтаксических ошибок не запуская сам скрипт, ключ –v выводит каждую команду до того как она будет выполнена, –x показывает результаты выполнения команд.

Например, если добавить любую некорректно сформированную строку в данный скрипт так, чтобы он не мог запуститься из-за синтаксической ошибки. Я просто добавил выражение "This is error for test" сразу после объявления функции start(). Теперь запустим его следующим образом:

bash –x anacron

В результате получили результаты выполнения команд, а также сообщение о синтаксической ошибке в строке №15 сценария.

Дополнительные возможности bash

На самом деле возможности Bash сильно отличаются от умений многих других интерпретаторов, именно поэтому этот интерпретатор в настоящее время стал стандартом де-факто в Linux-системах. Другим не менее распространенным шеллом является sh, который по умолчанию поставляется со всеми системными дистрибутивами. Но по удобству использования и по функциональности sh сильно уступает bash, поэтому последний набирает все больше голосов как среди программистов и администраторов, так и среди рядовых пользователей. Ниже приведен список (далеко не полный!) возможностей, имеющихся у bash 2.0 и отсутствующих у sh:

- наличие оператора выбора select;
- зарезервированное слово function для оформления функций;
- ведение истории команд;
- специальный синтаксис оператора цикла for для использования совместно с арифметическими операциями: for ((expr1 ; expr2; expr3 )); do list; done;
- перенаправление вывода: <>, &>, >|;
- режим posix для изменения поведения команд согласно стандарту
- расширение регулярных выражений для выполнения действий с подстроками (${p%[%]w}, ${p#[#]w});
- ловушка DEBUG trap;
- ловушка ERR trap;
- egrep-подобное расширение для поиска значения по образцу;
- возможность поиска без учета регистра;
- перенаправление в /dev/fd/N, /dev/stdin, /dev/stdout, /dev/stderr,

/dev/tcp/host/port, /dev/udp/host/port

Кроме того, bash имеет большое количество полезных переменных окружения: BASH, BASH_VERSION, BASH_VERSINFO, UID, EUID и т.п.

Подводим итоги

Как всегда после любой работы, нужно собрать урожай. А его немало: подробно рассмотрели код сценария автозапуска демона-планировщика anacron, убедились в силе и простоте программирования на скриптовых языках, узнали о нововведениях в bash 3.0 и об отладке только что написанного скрипта даже при отсутствии специальных возможностей отладки в старых версиях bash. Надеюсь, теперь ты избавился от фобии правки кодов имеющихся скриптов и даже сам можешь сварганить нечто подобное без особых проблем. Удачи!

Скрипты везде и всюду

Может быть, ты даже не подозреваешь, что языки описания сценариев используются практически везде. Любая операционная система уже в начале своей загрузки использует специальные текстовые файлы для настройки и оптимизации, а это и есть сценарии. Не забывай о роли скриптовых языков в web-программировании. В среде Linux повсеместно применяется мегапопулярное программирование на шелл, которое подходит для таких задач, как резервирование данных, обработка текста, управление учетными записями пользователей, настройка сетевого соединения, создание сценариев автозапуска, выполнение арифметических операций и многое другое.

Код сценария anacron

#!/bin/sh
# Startup script for anacron
#
# chkconfig: 2345 41 59
# description: Run cron jobs that were left out due to downtime
# Source function library.
 
. /etc/init.d/functions
[ -f /usr/sbin/anacron ] || exit
LOCKFILE=/var/lock/subsys/anacron
RETVAL=0
 
start() {
echo -n "Starting anacron: "
daemon anacron -s
RETVAL=$?
[ $RETVAL -eq 0 ] && touch "$LOCKFILE"
echo
}
 
stop() {
echo -n "Shutting down anacron: "
rm -f /var/run/anacron.pid
if [ -n "`pidof -o $$ -o $PPID -o %PPID -x anacron`" ]; then
killproc anacron
else
success "anacron shutdown"
fi
RETVAL=$?
[ $RETVAL -eq 0 ] && rm -f "$LOCKFILE"
echo
}
 
restart()
{
stop
start
}
 
# See how we were called.
case "$1" in
start)
start
;;
stop)
stop
;;
reload|restart)
restart
;;
condstop)
if [ -e "$LOCKFILE" ]; then
stop
fi
;;
condrestart)
if [ -e "$LOCKFILE" ]; then
restart
fi
;;
status)
status anacron
RETVAL=$?
;;
*)
echo "Usage: ${0##*/} {start|stop|reload|restart|condstop|condrestart|status}"
RETVAL=1
esac
exit $RETVAL

Bash 3.0 - два года спустя

Почти два года прошло после выхода предыдущей версии bash (2.0). Видимо, разработчики зря времени не теряли: в недавно вышедшей версии bash 3.0 появилось много чего нового. Исправлены многочисленные ошибки, улучшена поддержка многобайтовых символов, устранены некоторые несовместимости с POSIX, интегрирована возможность интернационализации сообщений и отладчик bash debugger.

Вот только неполный список нововведений в bash 3.0:

- расширение ANSI. Появилась возможность задания escape-последовательности в шестнадцатеричном виде \x{hexdigits};
- новая переменная COMP_WORDBREAKS, которая содержит набор символов для разделения слов;
- изменение формата записи символов в значении переменной HISTCONTROL;
- поддержка многобайтовых символов с появлением нового аргумента --enable-multibyte, который нужно указать для configure при установки bash;
- новые переменные для реализации встроенного отладчика bash: BASH_ARGC, BASH_ARGV, BASH_SOURCE, BASH_LINENO, ASH_SUBSHELL, BASH_EXECUTION_STRING, BASH_COMMAND;
- для операторов for, case, select, arithmetic commands теперь хранится специальная информация, используемая для отладки;
- новая ловушка RETURN, используемая при возвращении функции;
- новая опция bash для так называемой внешней отладки – debugger;
- добавлены операторы для упрощения и оптимизации применения регулярных выражений;
- интегрированы пакет Gettext и библиотека Libintl для перевода сообщений шелла на различные языки.

Полный перечень исправлений и новых функций ищи здесь: http://cnswww.cns.cwru.edu/~chet/bash/NEWS

Скачать tar.gz архив с исходниками bash любой версии, а также прочитать более подробную информацию ты можешь с сайта GNU Project www.gnu.org/software/bash/bash.html

Нужно учитывать, что строка #!/bin/sh на самом деле означает интерпретатор, использующийся в системе по умолчанию, которым в большинстве дистрибутивов Linux является bash.

При работе со строками в bash можно воспользоваться неинтерактивным строчным редактором sed и языком обработки шаблонов awk.

Определить версию bash, установленную у тебя, можно с помощью параметра --version.

В редакторе xemacs (emacs под X-Windows), помимо удобных средств редактирования, присутствуют также дополнительные средства отладки сценариев.
 
Опция -posix изменяет поведение bash в соответствии со стандартом 1003.2.
Content-Disposition: form-data; name="sort" 50


Источник: http://www.xakep.ru/magazine/xs/051/082/1.asp
Категория: Shell | Добавил: oleg (11.04.2008) | Автор: Андрей Семенюченко
Просмотров: 2357 | Комментарии: 1 | Рейтинг: 5.0/1 |
Всего комментариев: 1
1 n0name  
0
Вредная статья, чтобы не было проблем при запуске скрипта в системе где нет bash'a, писать нужно на sh.

Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Форма входа

Beastie

Друзья сайта

Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0
links

Copyright MyCorp © 2024