| Advanced Bash-Scripting Guide: Искусство программирования на языке сценариев командной оболочки | ||
|---|---|---|
| Назад | Вперед | |
В простейшем случае, скрипт -- это ни что иное, как простой список команд системы, записанный в файл. Создание скриптов поможет сохранить ваше время и силы, которые тратятся на ввод последовательности команд всякий раз, когда необходимо их выполнить.
Пример 2-1. cleanup: Сценарий очистки лог-файлов в /var/log
# cleanup
# Для работы сценария требуются права root.
cd /var/log
cat /dev/null > messages
cat /dev/null > wtmp
echo "Лог-файлы очищены."
Здесь нет ничего необычного, это простая последовательность команд, которая может быть набрана в командной строке с консоли или в xterm. Преимущество размещения последовательности команд в скрипте состоит в том, что вам не придется всякий раз набирать эту последовательность вручную. Кроме того, скрипты легко могут быть модифицированы или обобщены для разных применений.
Пример 2-2. cleanup: Расширенная версия предыдущего сценария.
#!/bin/bash
# cleanup, version 2
# Для работы сценария требуются права root.
LOG_DIR=/var/log
ROOT_UID=0 # Только пользователь с $UID 0 имеет привилегии root.
LINES=50 # Количество сохраняемых строк по-умолчанию.
E_XCD=66 # Невозможно сменить каталог?
E_NOTROOT=67 # Признак отсутствия root-привилегий.
if [ "$UID" -ne "$ROOT_UID" ]
then
echo "Для работы сценария требуются права root."
exit $E_NOTROOT
fi
if [ -n "$1" ]
# Проверка наличия аргумента командной строки.
then
lines=$1
else
lines=$LINES # Значение по-умолчанию, если число не задано в командной строке
fi
# Stephane Chazelas предложил следующее,
#+ для проверки корректности аргумента, переданного из командной строки,
#+ правда это достаточно сложно для данного руководства.
#
# E_WRONGARGS=65 # Не числовой аргумент
#
# case "$1" in
# "" ) lines=50;;
# *[!0-9]*) echo "Usage: `basename $0` file-to-cleanup"; exit $E_WRONGARGS;;
# * ) lines=$1;;
# esac
#
#* Конец проверки корректности аргумента
cd $LOG_DIR
if [ `pwd` != "$LOG_DIR" ] # или if [ "$PWD" != "$LOG_DIR" ]
# Не в /var/log?
then
echo "Невозможно перейти в каталог $LOG_DIR."
exit $E_XCD
fi # Проверка каталога перед очисткой лог-файлов.
# более эффективный вариант:
#
# cd /var/log || {
# echo "Невозможно перейти в требуемый каталог." >&2
# exit $E_XCD;
# }
tail -$lines messages > mesg.temp # Сохранить последние строки в лог-файле.
mv mesg.temp messages
# cat /dev/null > messages
#* Необходимость этой команды отпала, поскольку очистка выполняется выше.
cat /dev/null > wtmp # команды ': > wtmp' и '> wtmp' имеют тот же эффект.
echo "Лог-файлы очищены."
exit 0
# Возвращаемое значение 0
#+ указывает на успешное завершение работы сценария.
Если вы не желаете полностью вычищать системные логи, то выше представлена улучшенная версия предыдущего сценария. Здесь сохраняются последние несколько строк (по-умолчанию -- 50).
Если файл сценария начинается с последовательности #!, которая в мире UNIX называется sha-bang, то это указывает системе какой интерпретатор следует использовать для исполнения сценария. Это двухбайтовая последовательность, или [1] -- специальный маркер, определяющий тип сценария, в данном случае -- сценарий командной оболочки (см. man magic). Более точно, sha-bang определяет интерпретатор, который вызывается для исполнения сценария, это может быть командная оболочка (shell), иной интерпретатор или утилита. [2]
#!/bin/sh
#!/bin/bash
#!/usr/bin/perl
#!/usr/bin/tcl
#!/bin/sed -f
#!/usr/awk -f
Каждая, из приведенных выше сигнатур, приводит к вызову
различных интерпретаторов, будь то /bin/sh -- командный интерпретатор
по-умолчанию (bash для Linux-систем), либо иной. [3] При переносе сценариев с сигнатурой #!/bin/sh на другие UNIX
системы, где в качестве командного интерпретатора задан другой
shell, вы можете лишиться некоторых особенностей, присущих bash.
Поэтому такие сценарии должны быть POSIX совместимыми. [4].
Обратите внимание на то, что сигнатура должна указывать
правильный путь к интерпретатору, в противном случае вы получите
сообщение об ошибке -- как правило это "Command not found".
Сигнатура #! может быть опущена, если вы не
используете специфичных команд. Во втором примере (см. выше)
использование сигнатуры #! обязательно, поскольку сценарий
использует специфичную конструкцию присваивания значения
переменной lines=50. Еще раз
замечу, что сигнатура #!/bin/sh вызывает
командный интерпретатор по-умолчанию -- /bin/bash в Linux-системах.

В данном руководстве приветствуется модульный подход к
построению сценариев. Записывайте, собирайте свою
коллекцию участков кода, который может вам встретиться. В
конечном итоге вы соберете свою "библиотеку"
подпрограмм, которые затем сможете использовать при
написании своих сценариев. Например, следующий отрывок
сценария проверяет количество аргументов в командной
строке:
if [ $# -ne Number_of_expected_args ]
then
echo "Usage: `basename $0` whatever"
exit $WRONG_ARGS
fi
2.1. Запуск сценария
Запустить сценарий можно командой sh scriptname [5] или bash scriptname. (Не
рекомендуется запуск сценария командой sh <scriptname>,
поскольку это запрещает использование устройства стандартного
ввода stdin в скрипте). Более удобный вариант
-- сделать файл скрипта исполняемым, командой chmod.
- Это:
-
chmod 555
scriptname (выдача прав на
чтение/исполнение любому пользователю в системе) [6]
- или
-
chmod +rx
scriptname (выдача прав на
чтение/исполнение любому пользователю в системе)
chmod u+rx
scriptname (выдача прав на
чтение/исполнение только "владельцу"
скрипта)
После того, как вы сделаете файл сценария исполняемым, вы
можете запустить его примерно такой командой ./scriptname. [7] Если, при этом, текст сценария
начинается с корректной сигнатуры ("sha-bang"), то для его
исполнения будет вызван соответствующий интерпретатор.
И наконец, завершив отладку сценария, вы можете поместить
его в каталог /usr/local/bin (естественно, что для
этого вы должны обладать правами root), чтобы сделать его
доступным для себя и других пользователей системы. После этого
сценарий можно вызвать, просто напечатав название файла в
командной строке и нажав клавишу [ENTER].
| [1] |
Некоторые разновидности UNIX (основанные на 4.2BSD) требуют, чтобы эта последовательность состояла из 4-х байт, за счет добавления пробела после !, #! /bin/sh. |
| [2] |
В shell-скриптах последовательность #! должна стоять самой первой и задает интерпретатор (sh или bash). Интерпретатор, в свою очередь, воспринимает эту строку как комментарий, поскольку она начинается с символа #. Если в сценарии имеются еще такие же строки, то они воспринимаются как обычный комментарий.
#!/bin/bash
echo "Первая часть сценария."
a=1
#!/bin/bash
# Это *НЕ* означает запуск нового сценария.
echo "Вторая часть сценария."
echo $a # Значение переменной $a осталось равно 1.
|
| [3] |
Эта особенность позволяет использовать различные хитрости.
#!/bin/rm
# Самоуничтожающийся сценарий.
# Этот скрипт ничего не делает -- только уничтожает себя.
WHATEVER=65
echo "Эта строка никогда не будет напечатана."
exit $WHATEVER # Не имеет смысла, поскольку работа сценария завершается не здесь.
|
| [4] |
Portable Operating System Interface, попытка стандартизации UNIX-подобных операционных систем. |
| [5] |
Внимание: вызов Bash-скрипта с помощью команды sh scriptname отключает специфичные для Bash расширения, что может привести к появлению ошибки и аварийному завершению работы сценария. |
| [6] |
Сценарий должен иметь как право на исполнение, так и право на чтение, поскольку shell должен иметь возможность прочитать скрипт. |
| [7] |
Почему бы не запустить сценарий просто набрав название файла scriptname, если сценарий находится в текущем каталоге? Дело в том, что из соображений безопасности, путь к текущему каталогу "." не включен в переменную окружения $PATH. Поэтому необходимо явно указывать путь к текущему каталогу, в котором находится сценарий, т.е. ./scriptname. |