Добавить в цитаты Настройки чтения

Страница 99 из 113

Это очень специализированная проверка. Она возвращает код завершения 0, только если строка, введенная пользователем, содержит число в диапазоне от 0 до 3. Никакой другой ввод не принимается. Иногда писать такие проверки очень утомительно, но они совершенно необходимы, если вы хотите в результате получить надежно работающий сценарий.

Тестирование

Тестирование — важный этап в разработке любого программного обеспечения, включая сценарии. В мире открытого программного обеспечения в ходу высказывание «выпускай раньше, выпускай чаще», отражающее этот факт. Программное обеспечение, выпускаемое раньше и чаще, получает больше времени на использование и тестирование. Опыт показывает, что ошибки тем легче найти и тем дешевле исправить, чем раньше в цикле разработки они будут обнаружены.

Заглушки

Ранее мы продемонстрировали использование заглушек для проверки потока выполнения программы. Это ценный прием проверки прогресса в работе, начиная с самых ранних стадий разработки сценария.

Вернемся к уже рассматривавшейся проблеме определения присутствия каталога и посмотрим, как можно было бы легко протестировать ее решение. Тестировать оригинальный фрагмент довольно опасно, потому что его задача — удаление файлов, но его можно изменить, чтобы сделать тестирование безопасным:

if [[ -d $dir_name ]]; then

        if cd $dir_name; then

                echo rm * # ТЕСТИРОВАНИЕ

        else

                echo "ca

                exit 1

        fi

else

        echo "no such directory: '$dir_name'" >&2

        exit 1

fi

exit # ТЕСТИРОВАНИЕ

Так как проверка ошибочных условий уже выводит содержательные сообщения, нам не требуется добавлять ничего нового. Самое важное изменение заключается в добавлении команды echo перед командой rm, которая выведет ее и список ее аргументов, но не разрешит ей выполниться. Это изменение позволит безопасно выполнить код. В конец фрагмента мы добавили команду exit, чтобы завершить тест и предотвратить выполнение любых других частей сценария. Необходимость этого шага зависит от предназначения сценария.

Мы также включили несколько комментариев, которые служат «маркерами» изменений, имеющих отношение к тестированию. С их помощью легко можно найти и удалить эти изменения по завершении тестирования.

Комплекты тестов

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

• dir_name содержит имя существующего каталога;

• dir_name содержит имя несуществующего каталога;

• dir_name содержит пустое значение.

Проверив каждое из этих условий, мы получим приличный охват тестированием.

Так же как в случае с проектированием, тестирование есть функция от времени. Не каждую особенность сценария нужно тщательно тестировать. В действительности выбор фрагментов для тестирования зависит от того, что считается важным. Поскольку наш фрагмент может нести разрушительные последствия, он заслуживает и тщательного проектирования, и тщательного тестирования.

Отладка

Если тестирование выявляет проблему в сценарии, следующим шагом является отладка. Под «проблемой» обычно понимается несоответствие результатов работы сценария ожиданиям программиста. В этом случае нужно точно отследить, что сценарий делает и почему. Поиск ошибок иногда очень напоминает детективное расследование.

Тщательное проектирование сценария может помочь в этом. Согласно принципу защитного программирования, сценарий должен обнаруживать ненормальные условия и выводить содержательные сообщения. Иногда, однако, возникают странные и неожиданные проблемы, требующие применения более сложных приемов защиты.

Поиск проблемной области

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

if [[ -d $dir_name ]]; then

        if cd $dir_name; then

                rm *

        else

                echo "ca

                exit 1

        fi

# else

#         echo "no such directory: '$dir_name'" >&2

#         exit 1

fi



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

Трассировка

Ошибки часто становятся причиной неожиданного направления выполнения сценария. То есть фрагменты сценария могут никогда не выполняться или выполняться в неправильном порядке или в неправильные моменты. Чтобы увидеть, как в действительности протекает выполнение программы, воспользуемся приемом трассировки.

Один из способов трассировки заключается в размещении информативных сообщений в разных точках сценария, сообщающих, где протекает выполнение. Например, добавим в наш фрагмент следующие сообщения:

echo "preparing to delete files" >&2

if [[ -d $dir_name ]]; then

        if cd $dir_name; then

echo "deleting files" >&2

                rm *

        else

                echo "ca

                exit 1

        fi

else

        echo "no such directory: '$dir_name'" >&2

        exit 1

fi

echo "file deletion complete" >&2

Здесь сообщения посылаются в стандартный вывод ошибок, чтобы отделить их от обычного вывода. Кроме того, отсутствуют отступы перед строками с сообщениями, — это упростит их поиск, когда придет время убрать эти строки.

Теперь, запустив сценарий, убедимся, что удаление файлов действительно было выполнено:

[[email protected]/* */ ~]$ deletion-script

preparing to delete files

deleting files

file deletion complete

[[email protected]/* */ ~]$

Кроме того, bash поддерживает встроенный метод трассировки, реализованный в виде параметра -x и команды set с параметром -x. Возьмем для примера сценарий trouble, написанный ранее, и активируем встроенный механизм трассировки для всего сценария, добавив параметр -x в первую строку:

#!/bin/bash -x

# trouble: сценарий для демонстрации распространенных видов ошибок

number=1

if [ $number = 1 ]; then

        echo "Number is equal to 1."

else

        echo "Number is not equal to 1."

fi

После запуска мы получим следующие результаты:

[[email protected]/* */ ~]$ trouble

+ number=1

+ '[' 1 = 1 ']'

+ echo 'Number is equal to 1.'

Number is equal to 1.

Включенный механизм трассировки позволяет увидеть, какой вид приобретают команды после применения подстановки. Начальные знаки «плюс» помогают отличить трассировочную информацию от обычного вывода. Знак «плюс» — это символ по умолчанию, используемый для вывода трассировки. Он хранится в переменной командной оболочки PS4 (prompt string 4 — строка приглашения 4). Изменим значение этой переменной, чтобы сделать трассировочный вывод более полезным. Ниже мы изменили эту переменную, включив в трассировочный вывод текущий номер выполняемой строки в сценарии. Обратите внимание на необходимость использования одиночных кавычек — это предотвращает подстановку до момента, когда строка приглашения не будет использоваться фактически: