|
Поиск по сайту:
Главная
О проекте
Web-мастеру
HTML & JavaScript
SSI
Perl
PHP
XML & XSLT
Unix Shell
MySQL
Безопасность
Хостинг
Другое
|
|
Глава 29. Отладка сценариев
Командная оболочка Bash не имеет своего отладчика, и не
имеет даже каких либо отладочных команд или конструкций.
Синтаксические ошибки или опечатки часто вызывают сообщения
об ошибках, которые которые практически никак не помогают
при отладке.
Пример 29-1. Сценарий, содержащий
ошибку
#!/bin/bash
# ex74.sh
# Этот сценарий содержит ошибку.
a=37
if [$a -gt 27 ]
then
echo $a
fi
exit 0
В результате исполнения этого сценария вы получите такое
сообщение:
./ex74.sh: [37: command not found
Что в этом сценарии может быть неправильно (подсказка: после
ключевого слова if)?
Пример 29-2. Пропущено ключевое слово
#!/bin/bash
# missing-keyword.sh:
# Какое сообщение об ошибке будет выведено, при попытке запустить этот сценарий?
for a in 1 2 3
do
echo "$a"
# done # Необходимое ключевое слово 'done' закомментировано.
exit 0
На экране появится сообщение:
missing-keyword.sh: line 11: syntax error: unexpected end of file
Обратите внимание, сообщение об ошибке будет содержать номер не
той строки, в которой возникла ошибка, а той, в которой Bash точно
установил наличие ошибочной ситуации.
Сообщения об ошибках могут вообще не содержать номера
строки, при исполнении которой эта ошибка появилась.
А что делать, если сценарий работает, но не так как
ожидалось? Вот пример весьма распространенной логической
ошибки.
Пример 29-3. test24
#!/bin/bash
# Ожидается, что этот сценарий будет удалять в текущем каталоге
#+ все файлы, имена которых содержат пробелы.
# Но он не работает. Почему?
badname=`ls | grep ' '`
# echo "$badname"
rm "$badname"
exit 0
Попробуйте найти ошибку, раскомментарив строку echo "$badname".
Инструкция echo очень полезна при отладке сценариев, она
позволяет узнать -- действительно ли вы получаете то, что
ожидали получить.
В данном конкретном случае, команда rm "$badname" не
дает желаемого результата потому, что переменная $badname взята в кавычки. В результате,
rm получает единственный
аргумент (т.е. команда будет считать, что получила имя одного
файла). Частично эта проблема может быть решена за счет
удаления кавычек вокруг $badname и
установки переменной $IFS так, чтобы
она содержала только символ перевода строки, IFS=$'\n'. Однако,
существует более простой способ выполнить эту задачу.
# Правильный способ удаления файлов, в чьих именах содержатся пробелы.
rm *\ *
rm *" "*
rm *' '*
# Спасибо S.C.
В общих чертах, ошибочными можно считать такие сценарии,
которые
-
"сыплют" сообщениями о "синтаксических
ошибках" или
-
запускаются, но работают не так как ожидалось
(логические ошибки).
-
запускаются, делают то, что требуется, но имеют побочные
эффекты (логическая
бомба).
Инструменты, которые могут помочь при отладке неработающих
сценариев
-
команда echo, в критических точках сценария, поможет
отследить состояние переменных и отобразить ход
исполнения.
-
команда-фильтр tee,
которая поможет проверить процессы и потоки данных в
критических местах.
-
ключи -n -v -x
sh -n
scriptname -- проверит наличие синтаксических
ошибок, не запуская сам сценарий. Того же эффекта можно
добиться, вставив в сценарий команду set -n или set -o noexec. Обратите
внимание, некоторые из синтаксических ошибок не могут быть
выявлены таким способом.
sh -v
scriptname -- выводит каждую команду прежде,
чем она будет выполнена. Того же эффекта можно добиться,
вставив в сценарий команду set -v или set -o verbose.
Ключи -n и -v могут употребляться совместно: sh -nv scriptname.
sh -x
scriptname -- выводит, в краткой форме,
результат исполнения каждой команды. Того же эффекта можно
добиться, вставив в сценарий команду set -x или set -o xtrace.
Вставив в сценарий set
-u или set -o
nounset, вы будете получать сообщение об
ошибке unbound variable
всякий раз, когда будет производиться попытка обращения к
необъявленной переменной.
-
Функция "assert",
предназначенная для проверки переменных или условий, в
критических точках сценария. (Эта идея заимствована из
языка программирования C.)
Пример 29-4. Проверка условия с помощью
функции "assert"
#!/bin/bash
# assert.sh
assert () # Если условие ложно,
{ #+ выход из сценария с сообщением об ошибке.
E_PARAM_ERR=98
E_ASSERT_FAILED=99
if [ -z "$2" ] # Недостаточное количество входных параметров.
then
return $E_PARAM_ERR
fi
lineno=$2
if [ ! $1 ]
then
echo "Утверждение ложно: \"$1\""
echo "Файл: \"$0\", строка: $lineno"
exit $E_ASSERT_FAILED
# else
# return
# и продолжить исполнение сценария.
fi
}
a=5
b=4
condition="$a -lt $b" # Сообщение об ощибке и завершение сценария.
# Попробуйте поменять условие "condition"
#+ на что нибудь другое и
#+ посмотреть -- что получится.
assert "$condition" $LINENO
# Сценарий продолжит работу только в том случае, если утверждение истинно.
# Прочие команды.
# ...
echo "Эта строка появится на экране только если утверждение истинно."
# ...
# Прочие команды.
# ...
exit 0
-
Ловушка на выхто в этом сценарии может быть неправильно
(подсказка: после ключевого словоде.
Команда exit, в
сценарии, порождает сигнал 0, по которому процесс завершает
работу, т.е. -- сам сценарий. Часто бывает полезным по
выходу из сценария выдать "распечатку" переменных.
Установка ловушек на
сигналы
- trap
-
Определяет действие при получении сигнала; так же
полезна при отладке.
|
Сигнал (signal) -- это просто
сообщение, передается процессу либо ядром, либо
другим процессом, чтобы побудить процесс
выполнить какие либо действия (обычно --
завершить работу). Например, нажатие на
Control-C, вызывает передачу сигнала
SIGINT, исполняющейся программе.
|
trap '' 2
# Игнорировать прерывание 2 (Control-C), действие по сигналу не указано.
trap 'echo "Control-C disabled."' 2
# Сообщение при нажатии на Control-C.
Пример 29-5. Ловушка на выходе
#!/bin/bash
trap 'echo Список переменных --- a = $a b = $b' EXIT
# EXIT -- это название сигнала, генерируемого при выходе из сценария.
a=39
b=36
exit 0
# Примечательно, что если закомментировать команду 'exit',
# то это никак не скажется на работе сценария,
# поскольку "выход" из сценария происходит в любом случае.
Пример 29-6. Удаление временного файла при нажатии
на Control-C
#!/bin/bash
# logon.sh: Сценарий, написаный "на скорую руку", контролирует вход в режим on-line.
TRUE=1
LOGFILE=/var/log/messages
# Обратите внимание: $LOGFILE должен быть доступен на чтение (chmod 644 /var/log/messages).
TEMPFILE=temp.$$
# "Уникальное" имя для временного файла, где расширение в имени -- это pid процесса-сценария.
KEYWORD=address
# При входе, в файл /var/log/messages,
# добавляется строка "remote IP address xxx.xxx.xxx.xxx"
ONLINE=22
USER_INTERRUPT=13
CHECK_LINES=100
# Количество проверяемых строк.
trap 'rm -f $TEMPFILE; exit $USER_INTERRUPT' TERM INT
# Удалить временный файл, когда сценарий завершает работу по control-c.
echo
while [ $TRUE ] #Бесконечный цикл.
do
tail -$CHECK_LINES $LOGFILE> $TEMPFILE
# Последние 100 строк из системного журнала переписать во временный файл.
# Совершенно необходимо, т.к. новейшие версии ядер генерируют много сообщений при входе.
search=`grep $KEYWORD $TEMPFILE`
# Проверить наличие фразы "address",
# свидетельствующей об успешном входе.
if [ ! -z "$search" ] # Кавычки необходимы, т.к. переменная может содержать пробелы.
then
echo "On-line"
rm -f $TEMPFILE # Удалить временный файл.
exit $ONLINE
else
echo -n "." # ключ -n подавляет вывод символа перевода строки,
# так вы получите непрерывную строку точек.
fi
sleep 1
done
# Обратите внимание: если изменить содержимое переменной KEYWORD
# на "Exit", то сценарий может использоваться для контроля
# неожиданного выхода (logoff).
exit 0
# Nick Drage предложил альтернативный метод:
while true
do ifconfig ppp0 | grep UP 1> /dev/null && echo "соединение установлено" && exit 0
echo -n "." # Печать последовательности точек (.....), пока соединение не будет установлено.
sleep 2
done
# Проблема: Нажатия Control-C может оказаться недостаточным, чтобы завершить этот процесс.
# (Точки продолжают выводиться на экран.)
# Упражнение: Исправьте этот недостаток.
# Stephane Chazelas предложил еще одну альтернативу:
CHECK_INTERVAL=1
while ! tail -1 "$LOGFILE" | grep -q "$KEYWORD"
do echo -n .
sleep $CHECK_INTERVAL
done
echo "On-line"
# Упражнение: Найдите сильные и слабые стороны
# каждого из этих подходов.
|
Аргумент DEBUG, команды
trap, заставляет
сценарий выполнять указанное действие после выполнения
каждой команды. Это можно использовать для трассировки
переменных.
Пример 29-7. Трассировка
переменной
#!/bin/bash
trap 'echo "VARIABLE-TRACE> $LINENO: \$variable = \"$variable\""' DEBUG
# Выводить значение переменной после исполнения каждой команды.
variable=29
echo "Переменная \"\$variable\" инициализирована числом $variable."
let "variable *= 3"
echo "Значение переменной \"\$variable\" увеличено в 3 раза."
# Конструкция "trap 'commands' DEBUG" может оказаться очень полезной
# при отладке больших и сложных скриптов,
# когда размещение множества инструкций "echo $variable"
# может потребовать достаточно большого времени.
# Спасибо Stephane Chazelas.
exit 0
|
|
Конструкция trap
'' SIGNAL (две одиночных кавычки)
-- запрещает SIGNAL для оставшейся части сценария.
Конструкция trap
SIGNAL -- восстанавливает действие
сигнала SIGNAL. Эти конструкции могут использоваться
для защиты критических участков сценария от
нежелательного прерывания.
|
trap '' 2 # Сигнал 2 (Control-C) -- запрещен.
command
command
command
trap 2 # Разрешение реакции на Control-C
Назад | Вперед
Содержание (общее) | Содержание раздела
Если Вы не нашли что искали, то рекомендую воспользоваться поиском по сайту:
|
|
|