Главная > Операционные системы > UNIX
Описание: Большинство задач системного администрирования могут быть автоматизарованы с использованием shell-скриптов. Кандидат BSDA должен знать о преимуществах и недостатках использования скриптов Bourne shell более, нежели csh(1) или bash(1). Кандидат должен различать «магическую строку» (shebang), комментарии, позиционные параметры и специальные параметры, маски в шаблонах, знать как правильно использовать кавычки и обратные слеши, операторы for, while, if, case и esec. Кандидат должен знать как сделать скрипт исполнимым и как его отлаживать. Практика: sh(1), chmod(1)
Комментарий
sh(1) используется не столько как интерактивный shell (надо признать, что sh(1) в работе не так удобен, как его более современные аналоги), сколько как язык программирования используемый для автоматизации рутинных процедур. На sh(1) написано множество скриптов используемых при старте системы, а так же скриптов обслуживающих её функционирование.
В первом приближнии sh(1) позволяет попросту перечислить внешние команды, которые необходимо выполнить. Оданко sh(1) так же обладает возможностью делать проверки, выполнять циклы, обрабатывать исключительные ситуации (перехватывать сигналы) и многими другими возможностями. Не стоит забывать, однако, что sh(1), это всего лишь удобное средство автоматизации. Несмотря на циклы и логику, несмотря на наличие собственной целочисленной арифметики, главное, для чего используется sh(1) — это вызов внешних программ, которые, собственно, и делают основную работу.
С чего начинается скрипт? С некоторой магической строки — shebang. Вообще, последовательность действий операционной системы с файлом при попытке пользователя запустить его на исполнение, выглядит примерно так:
Даже несмотря на наличие такого умолчания, разумно указывать
интерпретатор в явном виде. Сценарии sh должны начинаться со
строки
На вход интерпретатору подаётся весь скрипт целиком, включая
первую строку с shebang. Поэтому скриптовый язык обязан
воспринимать знак #!/bin/cat Hello При попытке выполнить скрипт мы получим:
Ещё одной особенностью скрипта является то, что он обязан иметь пермиссии не только на исполнение, но и на чтение. В противном случае скрипт не сможет быть направлен на стандартный вход указанному интерпретатору. В разделе посвящённом переменным окружения обсуждается вопрос использования утилиты env(1) в shebang и вопросы связанные с безопасностью при использовании различных интерпретаторов в «магической строке». Надо честно признать, повседневно работать в sh(1) неудобно. Да, sh(1) неудобный интерпретатор, ему есть множество альтернатив, которые, к тому же, порой полностью совместимы по синтаксису. Почему же в операционных системах BSD так упорно цепляются за него? Для начала приведу выдержку из FreeBSD FAQ.
Думается, что слова эти нуждаются в некотором дополнительном
пояснении. Когда UNIX запускает новый
процесс, ядро осуществляет следующую сложную цепочку действий:
сперва оно выполняет системный вызов fork(2)
и копирует область памяти соответствующую родительскому
процессу. Появляется два совершенно одинаковых родительских
процесса, которые различаются только кодом возврата функции
fork(2). Родитель получает
Из сказанного должно быть ясно, что во-первых вызов новой
программы, это очень дорогая операция (мы видели это, когда
сравнивали работу аргумента Сравним работу двух одинаковых с виду программ на sh: #!/bin/sh i=0 while [ $i -lt 1000 ] do i=`echo $i+1|/usr/bin/bc` done echo $i и на bash: #!/usr/local/bin/bash i=0 while [ $i -lt 1000 ] do i=`echo $i+1|/usr/bin/bc` done echo $i Как видите, эти программы отличаются только первой строчкой. В этих программах осуществляется в цикле тысячекратный вызов программы bc(1), крохотного калькулятора, для увеличения счётчика. И столько же раз вызвана команда test(1) (см. ниже).
Легко видеть, что тяжеловесный bash(1) ворочал этот скрипт почти вдвое дольше. На разных опробованных мною системах соотношение времени выполнения этого скрипта bash/sh колебалось от 1.3 до 1.9. Таблица 7.7. Синтаксическая таблица Bourne Shell
Присваивание переменной осуществляется при помощи оператора
Следует подчеркнуть: переменные и переменные окружения это не одно и то же. Любая переменная окружения видна как переменная, но не любая переменная видна как переменная окружения. Чтобы переменная стала переменной окружения её надо экспортировать:
Т.е. до вызова команды Таблица 7.8. Специальные переменные в Bourne Shell
В качестве условия в sh(1) выполняется некоторая команда, и изучается её код возврата. Если код возврата равен нулю, и, следовательно, программа завершилась успешно, sh(1) трактует это как истину, если программа вернула код возврата больше нуля, sh(1) трактует это как ложь.
Проверки можно комбинировать при помощи знаков
В простейшем случае можно вообще обойтись без явного условного оператора, используя лишь комбинацию этих знаков: uname | grep -q BSD && echo "Это какая-то BSD!" || echo "Не знаю что это."
Такой синтаксис краток, но не следует им злоупотреблять. В
конечном счёте это ведёт к трудно читаемым программам. Ту же
проверку лучше осуществить с использованием явного оператора
if uname | grep -q BSD then echo "Это какая-то BSD!" else echo "Не знаю, что это." fi
Если эти операторы понадобится написать в одну строку, то надо
понимать, что ключевые слова
Ветвь if uname | grep -q FreeBSD then echo "Это FreeBSD, на ней может получиться удобная рабочая станция" elif uname | grep -q OpenBSD then echo "Это OpenBSD, знаменитая своей безопасностью." echo "Прекрасный выбор для сервера" elif uname | grep -q NetBSD then echo "Это NetBSD, она поддерживает самые немыслимые архитектуры." echo "Хороший выбор для тостера или холодильника." else echo "Не знаю, что это." fi Недостатком данной конструкции является то, что здесь шесть раз вызываются программы uname(1) и grep(1). Существует более очевидная конструкция для проверки на соответвие строки списку значений. case "`uname`" in FreeBSD) echo "Это FreeBSD, на ней может получиться удобная рабочая станция";; OpenBSD) echo "Это OpenBSD, знаменитая своей безопасностью." echo "Прекрасный выбор для сервера";; NetBSD) echo "Это NetBSD, она поддерживает самые немыслимые архитектуры." echo "Хороший выбор для тостера или холодильника.";; BSD|[Dd][Aa][Rr][Vv][Ii][Nn]) echo "Есть основания полагать, что это тоже BSD";; *) echo "Не знаю, что это.";; esac Специально для условного оператора sh(1) существует программа осуществляющая математические проверки, проверки на существование файловых объектов и равенство строк. В зависимости от результата сравнения эта программа возвращает либо ноль, либо единицу. Речь идёт о программе test(1). Таблица 7.9. Опции команды test(1)
Для команды test(1) существует альтернативное имя [. Если она вызывается по имени [, то она разбирает командную строку вплоть до того, пока не встретит закрывающую квадратную скобку. Таким образом, следующие четыре конструкции эквивалентны:
Обратите внимание: вокруг квадратных скобок обязательно должны быть пробелы, потому что [ это не синтаксическая конструкция sh(1), а обычная команда на подобии test(1). В sh(1) имеется два вида циклов: цикл с условием и цикл с перебором. Первый действует до тех пор, пока верно некоторое условие. Второй перебирает значения некоторого списка, приравнивая переменную (итератор) каждый раз к новому значению из этого списка. Кроме того, имеются обычные команды для прерывания цикла.
Следующая программа выводит список квадратов натуральных
чисел от 1 до 100, используя цикл с условием i=1 while [ $i -le 100 ] do echo $(($i*$i)) i=$(($i+1)) done
Ключевые слова i=1; while [ $i -le 100 ]; do echo $(($i*$i)); i=$(($i+1)); done
Цикл может быть прерван встроенной командой i=1 while : do echo $(($i*$i)) i=$(($i+1)) if [ $i -gt 100 ]; then break; fi done
Встроенная команда
Встроенная команда
Следующая программа конвертирует все картинки в каталоге
for filename in image/*.jpg do # Действительно ли это JPEG? if ! file $filename | grep -q "JPEG image data" then continue fi # Конвертируем JPEG в PNG if convert $filename ${filename%jpg}png then echo "$filename -> ${filename%jpg}png" else echo "$filename don't converted" fi done Утилита convert(1) — сторонняя утилита. Не входит ни в одну операционную систему BSD по-умолчанию и доставляется отдельно из портов или пакетов.
Конструкция
Первая проверка нужна для того, чтобы убедиться, что мы
имеем дело с файлом в формате JPEG.
Вдруг файл имеющий это расширение на самом деле никакой не
JPEG, а, скажем MP3?
В случае, если это не JPEG мы
используем прерывание текущей итерации (но не всего цикла
целиком) при помощи команды Обратите внимание на использование кавычек: внутри двойных кавычек переменные раскрываются в свои значения. Кавычки мы использовали для того, чтобы в одном случае строка трактовалась бы утилитой grep(1) как один аргумент, несмотря на наличие в ней пробелов, в другом случае, чтобы защитить символ > и в третьем случае защитить одинарную кавычку.
Конструкция
sh(1) позволяет определять функции и
вызывать их. Переменные sqrt () { echo $(($1*$1)) } i=1 while [ $i -le 100 ] do sqrt $i i=$(($i+1)) done При помощи директивы return функция может вернуть свой собственный код возврата.
В sh(1) существует конструкция,
позволяющая подгружать внешние файлы с определёнными в них
функциями и переменными. Допустим у нас есть файл
# Описываем константы PI=3.1415926535897931 # Функция для возведения в квадрат sqrt () { echo $(($1*$1)) } # Длина окружности circlen () { echo "$PI*$1*2" | /usr/bin/bc } # Площадь круга circarea () { sq=`sqrt $1` echo "$PI*$sq" | /usr/bin/bc }
Теперь, если мы захотим вычислить длину окружности, или её
площадь, нам достаточно внутри скрипта подгрузить данный
«модуль». Это делается при помощи оператора #!/bin/sh . math echo "Длина окружности радиуса 3 см равна `circlen 3` см" echo "А площадь круга того же радиуса равна `circarea 3` см^2" echo "Причина этого явления в том, что число пи, по прежнему" echo "равно $PI, и со времён древних греков" echo "существенно не изменилось..."
В этом скрипте мы подгружаем «модуль» math и
вызываем функцию подсчёта длины окружности и площади круга,
которые в нём определены, а так же ссылаемся на определённую
в нём переменную ................................. inetd_enable="NO" # Run the network daemon dispatcher (YES/NO). inetd_program="/usr/sbin/inetd" # path to inetd, if you want a different one. inetd_flags="-wW -C 60" # Optional flags to inetd ................................. Во втором они могут частично переопределяться, например: ................................. inetd_enable="YES" .................................
При этом оба файла последовательно подгружаются в файле
Команда getopts входит в стандарт
POSIX и является встроенной командой
sh(1). У команды
getopts имеется два аргумента:
1) — строка с перечнем возможных опций. После
опций у которых возможно значение, ставится двоеточие.
2) — имя переменной, в которую будет
сохраняться имя опции. Значение переменной будет
сохраняться в переменной #!/bin/sh while getopts e:h option do case $option in h) echo "Usage: `/usr/bin/basename $0` [-h|-e text]";; e) echo Hello, $OPTARG;; \?) $0 -h;; esac done
Теперь вызовем этот скрипт (назовём его
Команда eval просто выполняет свой аргумент. Это позволяет «сконструировать скрипт» складывая команды и аргументы в некоторую переменную, а потом подставить её на выполнение команде eval.
Ниже приведено другое остроумное применение данной
команды. Пример взят из файла
# # checkyesno var # Test $1 variable, and warn if not set to YES or NO. # Return 0 if it's "yes" (et al), nonzero otherwise. # checkyesno() { eval _value=\$${1} debug "checkyesno: $1 is set to $_value." case $_value in # "yes", "true", "on", or "1" [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) return 0 ;; # "no", "false", "off", or "0" [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0) return 1 ;; *) warn "\$${1} is not set properly - see rc.conf(5)." return 1 ;; esac } Встроенная в sh(1) команда trap позволяет зарегистрировать обработчик сигнала. Если в процессе выполнения скрипт получит указанный в команде trap сигнал, то вместо обычного поведения, он вызовет указанный обработчик: terminator () { echo "Не умру ни за что" >&2 } trap terminator 15
Теперь, если скрипт получит сигнал
Более разумным применением этого механизма было бы стирание временных файлов и корректное завершение работы. В заключение следует сказать, что после того, как вы написали могучий сценарий sh(1), ему не мешает дать пермиссии на выполнение, командой chmod(1).
Главная > Операционные системы > UNIX |