Программирование в оболочке UNIX`а

Привет! Сегодня я хочу рассказать о программировании, только программировании особенном - в командной оболочке Unix`a, или иначе (на фиглишском) про shell-coding.


Что такое Шелл-Кодинг?
Строго говоря, существуют различные толкования этого термина. Некоторые понимают под этим программирование командных оболочек под Юникс. Я же собираюсь рассказать о программировании сценариев на языке данной командной оболочки.


Для чего это нужно?
Естественно, для автоматизации каких-либо процессов. К слову, любой скрипт как раз и служит для автоматизации процессов. Например, если ты часто переименовываешь группы файлов, то будет разумным написать сценарий, который будет это делать, вызываясь всего лишь одной короткой строчкой:
renamer_scen -<список файлов>
Однако, вы можете возразить, что для этой цели можно использовать Perl. Что ж, соглашусь с вами, однако есть одно "но", а именно - скорость. Сценарий командной оболочки выполняется быстрее, чем тот же сценарий, написанный на Perl. Следующий момент заключается вот в чем: шелл-коды присутствуют во всех UNIX-системах. Следовательно, для понимания того, что делает тот или иной скрипт, ты должен разбираться в том языке, на котором данный сценарий написан.



Интерпретаторы.
Существует несколько популярных программ, в которых можно писать скрипты для оболочек юникса. Самый первый - Bourne Shell (sh). Появился вместе с первыми юниксами :). Сейчас на его место пришли более навороченные интерпретаторы. В дальнейшем появилась усовершенствованная версия, которая называется Bourne Again Shell (bash) - наиболее часто встречаемый интерпретатор в различный версиях Линукса.


Вторая ветвь данной программы - это C Shell (csh). Стоит по умолчанию на BSD системах. Кстати говоря, содержит энное число ошибок, которые могут привести к краху системы. Усовершенствованной версией csh является tsch – ошибки исправлены, добавлены некоторые возможности.



Итак, начнем. Я буду рассказывать про sh-оболочку, так как думаю, что для начальных задач вам этого вполне должно хватить. Да и большинство системных скриптов написано именно под него.



Запуск сценария.
Как и для любого интерпретатора, надо указать программу, которая будет выполнять ваш скрипт:
sh my_script.sh
Второй вариант запуска - указать в начале файла, чем его следует открывать:
#!/bin/sh
Кстати, перед запуском нашу программу необходимо сделать исполняемой:
chmod +x my_script.sh
Рассмотрим подробнее, как в данном случае будет работать скрипт. При чтении #! запускается указанная после этих символов программа, и ей в параметрах передается наш скрипт, а точнее его имя. Помимо sh, можно включать и другие программы. Например, если тебе требуется обработать что-то в awk, то эффективнее будет составить такой скрипт:
#!/usr/bin/awk [parametrs]
Скрипт будет выполняться быстрее. Такой прирост производительности объясняется тем, что awk вызывается в память напрямую, а не через sh. Для сравнения рассмотрим два таких сценария:
#!/bin/sh
#sh_script.sh
echo %0

Запускаем программу echo через sh и выводим на экран имя вызывающего сценария.

#!/bin/echo
#script.sh

Запускаем непосредственно echo (тело можно оставить пустым, так как выполняется /bin/echo %0).

Сравним время выполнения обоих скриптов:
time ./sh_script.sh && time ./script.sh
Получаем, что на выполнение второго сценария потрачено примерно в два-три раза меньше машинного времени (к слову, на разных компьютерах это время разное).



Отладка скрипта.
Для отладки sh вызывается с двумя аргументами:
- -xv (-x и -v)
Выглядит так:
#!/bin/sh -xv
В результате при выполнении программы будет видно, какие именно команды выполняются в данный момент.



Переменные.
Тут все просто и понятно:
$some_varible = value


C переменной после ее объявления можно проводить все общепринятые действия, например, вывести ее на экран:
echo $some_varible



Конструкция управления CASE.
Синтаксис:
case "<переменная>" in
<значение1> ) <действие1>;;
<значение2> ) <действие2>;;
*) <действие по дефолту>;;
esac

Значение переменной сравнивается с вариантами значений. Если не совпадает ни с одним, то выполняется действие по умолчанию. В данном случае можно использовать стандартные метасимволы (такие как *, [0-3], etc, а так же:?*) - любая непустая строка (1 и более символов).
(Y|YES) - Y ИЛИ YES
([Yy]) - y или Y



Конструкции условия IF-FI.
Синтаксис:
if list1; then
<действие 1>
elif list2; then
<действие 2>
else
<действие 3>
fi

Если список list1 возвращает TRUE, то выполняется действие 1, и так далее.



Циклы.
Для начала рассмотрим три вида данных конструкций.


FOR
for <имена переменных> in <строка 1>
do
<действие>
done

При каждом проходе цикла переменной присваивается значение из строки. Например, такой скрипт:
for i in 1 2 3 4
do
echo $i
done

выводит числа в столбик от одного до четырех.


WHILE
Синтаксис:
while <список команд1>
do
<список команд2>
done

Пока список команд1 возвращает истину, выполняется список команд2.


UNTIL
Синтаксис:
until <список команд1>
do
<список команд2>
done

Список команд2 выполняется до тех пор, пока список команд1 не вернет истину. Во всех конструкциях циклов можно использовать операторы break и continue для прерывания цикла или перехода к новой итерации соответственно.



Чтение данных с клавиатуры.
Для чтения данных используется команда
read <переменная>
Если указать несколько переменных, то первое введенное значение запишется в первую переменную, второе во вторую, и так далее.


Пример:
while read i
do
if i==’x’ then
break
fi
echo $i
done

Цикл выводит на экран все, что ввел пользователь, и завершается при нажатии на X.



Специальные команды.
$0
Команда, по которой был вызван данный сценарий (нулевой аргумент шелла).
$n
n-ый аргумент шелла. Например, для
./my_script some_args
$1 будет соответствовать some_args.
$#
количество аргументов сценария.
$@
Все параметры, переданные скрипту. Каждый из них заключен в кавычки "".
$*
Все параметры, переданные скрипту. ВСЕ параметры заключены в кавычки "".
$$
текущий PID (оболочки, так как сценарий запущен из-под нее).
$?
Код завершения последней команды. Если >0, то, скорее всего, завершение было аварийным. Кроме того, код завершения можно устанавливать вручную, используя команду
exit
Функция
shift
удаляет первый аргумент и сдвигает все остальные на 1 позицию влево. Эта команда используется, если мы хотим получить путь и имя скрипта:
/home/user/my_script.sh
Вместе в $0 удобно использовать команды
basename
и
dirname

Они возвращают имя и путь соответственно:
my_script.sh
/home/user/



Обработка сигналов.
Для обработки сигналов используется команда trap.
trap <действие> <список сигналов>
Действие выполняется, если скрипту послали указанные сигналы или сигнал.
trap “Don’t touch keyboard” 15
Выводится сообщение при получении 15 сигнала.



Функции.
С ними все просто.
Function test() {
echo "Test complete"
exit 0;
}

Функция вызывается, если в качестве команды указать имя функции.



Подстановка параметров-переменных в sh.
Конструкции для подстановки значений в переменные:
{variable:-default_value}
Если переменная не определена, то ИСПОЛЬЗУЕТСЯ переменная default_value.
{variable:=default_value}
Если переменная не определена, то ей ПРИСВАИВАЕТСЯ значение переменной default_value.
{variable+=value}
Если переменная определена и не пустая, то ИСПОЛЬЗУЕТСЯ переменная value.
{variable:?message}
Если переменная определена и не пустая, она просто используется. В противном случае выводится message.



Перенаправление потоков.
В sh очень удобно организовано перенаправление потоков. Есть 3 стандартных потока:
stdin(standart input)
stdiout(standart output)
stderr(standart error)

Им обычно присваиваются стандартные дескрипторы файлов 0, 1 и 2 соответственно.


Направление можно переопределять. Например, чтобы направить весь вывод ошибок какой-либо команды в файл, нужно сделать следующее:
<команда> 2><файл с ошибками>


Кроме оператора > стандартный ввод-вывод можно переадресовывать с помощью каналов |:
<команда1> | <команда2>
Весь стандартный вывод команды1 попадет на стандартный ввод команды2.


Также существует возможность открыть дополнительные потоки с номерами от 3 до 9. Например, можно направить поток 3 туда, куда направлен поток 1, написав такую команду:
3>&1



Вот, собственно, и все. Я разобрал здесь лишь sh, однако, зная основы, вы можете легко изучить языки других оболочек самостоятельно…


K0r0l


Автор: K0r0l , @