Рейтинговые книги
Читем онлайн Командная строка Linux. Полное руководство - Уильям Шоттс

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 78 79 80 81 82 83 84 85 86 ... 93

Number is not equal to 1.

Мы получили довольно загадочное сообщение, за которым следует вывод второй команды echo. Проблема заключается в подстановке переменной number в команду test. После обработки команды

[ $number = 1 ]

механизмом подстановки, который заменит number пустым значением:

[ = 1 ]

получится недопустимый результат, и командная оболочка сгенерирует сообщение об ошибке. Оператор = является бинарным (он требует наличия двух операндов, по одному с каждой стороны), но первое значение отсутствует, поэтому коман­да test ожидает встретить унарный оператор (такой, как -z). Далее, поскольку test вернула ненулевой код завершения (из-за ошибки), команда if получит ненулевой код завершения, примет соответствующее решение и выполнит вторую команду echo.

Эту проблему можно исправить, заключив в кавычки первый аргумент команды test:

[ "$number" = 1 ]

Теперь подстановка приведет к следующему результату:

[ "" = 1 ]

с правильным числом аргументов. Кавычки следует использовать не только для предохранения от пустых строк, но и в том случае, если переменная содержит строку с несколькими словами, например имя файла со встроенными пробелами.

Логические ошибки

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

• Неправильное условное выражение. Очень легко неправильно запрограммировать оператор if/then/else и получить ошибочную логику работы. Иногда логика получается полностью обратной желаемой или не охватывает весь возможный набор ситуаций.

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

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

Защитное программирование

При программировании важно не опираться на допущения, то есть тщательно проверять коды завершения программ и команд, используемых сценарием. Вот пример из реальной жизни. Системный горе-администратор написал сценарий, выполняющий некую административную задачу на очень важном сервере. Этот сценарий содержал следующие две строки кода:

cd $dir_name

rm *

В самих строках нет никакой ошибки, при условии, что каталог, указанный в переменной dir_name, действительно существует. Но что случится, если это не так? Тогда команда cd потерпит неудачу, сценарий перейдет к следующей строке и удалит файлы в текущем рабочем каталоге. Результат, как вы понимаете, далек от ожидаемого! Несчастный администратор уничтожил массу важных файлов на сервере из-за этой логической ошибки.

Рассмотрим несколько способов усовершенствования описанной логики. Прежде всего, можно поставить вызов команды rm в зависимость от успеха cd:

cd $dir_name && rm *

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

[[ -d $dir_name ]] && cd $dir_name && rm *

В подобных ситуациях, как описанных выше, лучше прервать выполнение сценария с выводом сообщения об ошибке:

if [[ -d $dir_name ]]; then

if cd $dir_name; then

rm *

else

echo "cannot cd to '$dir_name'" >&2

exit 1

fi

else

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

exit 1

fi

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

Проверка ввода

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

[[ $REPLY =~ ^[0-3]$ ]]

удачный дизайн есть функция от времени

Когда я в студенчестве изучал промышленное проектирование, мудрый профессор учил нас, что степень проработки проекта определяется объемом времени, выделенного проектировщику. Если вам дано 5 минут на проектирование устройства для уничтожения воздушных целей, вы спроектируете мухобойку. А если срок — 5 месяцев, вы сможете спроектировать лазерную систему противовоздушной обороны.

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

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

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

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

Заглушки

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

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

if [[ -d $dir_name ]]; then

if cd $dir_name; then

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

else

echo "cannot cd to '$dir_name'" >&2

exit 1

fi

else

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

exit 1

fi

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

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

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

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

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

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

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

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

1 ... 78 79 80 81 82 83 84 85 86 ... 93
На этой странице вы можете бесплатно читать книгу Командная строка Linux. Полное руководство - Уильям Шоттс бесплатно.
Похожие на Командная строка Linux. Полное руководство - Уильям Шоттс книги

Оставить комментарий