Командная строка Linux. Полное руководство - Уильям Шоттс
Шрифт:
Интервал:
Закладка:
Please Select:
1. Display System Information
2. Display Disk Space
3. Display Home Space Utilization
0. Quit
_EOF_
read -p "Enter selection [0-3] > "
if [[ $REPLY =~ ^[0-3]$ ]]; then
if [[ $REPLY == 1 ]]; then
echo "Hostname: $HOSTNAME"
uptime
sleep $DELAY
continue
fi
if [[ $REPLY == 2 ]]; then
df -h
sleep $DELAY
continue
fi
if [[ $REPLY == 3 ]]; then
if [[ $(id -u) -eq 0 ]]; then
echo "Home Space Utilization (All Users)"
du -sh /home/*
else
echo "Home Space Utilization ($USER)"
du -sh $HOME
fi
sleep $DELAY
continue
fi
if [[ $REPLY == 0 ]]; then
break
fi
else
echo "Invalid entry."
sleep $DELAY
fi
done
echo "Program terminated."
В этой версии сценария используется бесконечный цикл (цикл, который никогда не завершится сам по себе), в котором команда while проверяет код завершения команды true. Так как true всегда возвращает код 0, цикл никогда не завершится. Этот прием на удивление широко используется в сценариях. Поскольку цикл никогда не завершится сам по себе, программист должен предусмотреть его принудительное прерывание в нужный момент времени. В этом сценарии выход из цикла осуществляется с помощью команды break, когда пользователь выберет пункт 0. В конец других операций добавлена команда continue, чтобы увеличить эффективность работы сценария. Встретив команду continue, сценарий перепрыгнет через остальной код в цикле, который не требуется выполнять для данного выбора. Например, если пользователь выбрал пункт 1, нет никаких причин проверять выбор остальных вариантов.
until
Команда until очень похожа на while, но завершает цикл не когда обнаружит ненулевой код завершения, а наоборот. Цикл until продолжается, пока не получит код завершения 0. В сценарии while-count цикл продолжает выполняться, пока значение переменной count меньше или равно 5. Тот же результат можно получить, переписав сценарий с командой until:
#!/bin/bash
# until-count: вывод последовательности чисел
count=1
until [ $count -gt 5 ]; do
echo $count
count=$((count + 1))
done
echo "Finished."
С условным выражением $count -gt 5 команда until завершит цикл в нужный момент времени. Выбор между циклами while и until обычно зависит от того, в каком случае условное выражение будет более читабельным.
Чтение файлов в циклах
Команды while и until могут принимать данные со стандартного ввода. Это дает возможность обрабатывать файлы с их помощью. В следующем примере мы выведем содержимое файла distros.txt, созданного в одной из предыдущих глав:
#!/bin/bash
# while-read: чтение строк из файла
while read distro version release; do
printf "Distro: %stVersion: %stReleased: %sn"
$distro
$version
$release
done < distros.txt
Чтобы перенаправить файл в цикл, мы поместили оператор перенаправления после инструкции done. Цикл будет вводить поля из указанного файла с помощью read. После ввода каждой строки команда read будет завершаться с кодом 0, пока не достигнет конца файла. В этот момент она вернет ненулевой код завершения, и цикл завершится. Цикл можно также использовать в конвейерах:
#!/bin/bash
# while-read2: чтение строк из файла
sort -k 1,1 -k 2n distros.txt | while read distro version release; do
printf "Distro: %stVersion: %stReleased: %sn"
$distro
$version
$release
done
Здесь вывод команды sort передается на стандартный ввод цикла, который выводит поток текста на экран. Но не забывайте, что конвейер выполняет цикл в подоболочке, поэтому после его завершения любые переменные, созданные в цикле, будут потеряны.
Заключительное замечание
После знакомства с циклами и ранее представленными командами ветвления, функциями и последовательностями мы получили представление об основных способах управления потоком выполнения в программах. В арсенале bash имеется еще множество хитрых трюков, но все они основаны на этих простых идеях.
30. Поиск и устранение ошибок
Поскольку наши сценарии становятся все сложнее и сложнее, настало время посмотреть, что случается, когда что-то идет не так и сценарии перестают делать то, что нам нужно. В этой главе мы познакомимся с некоторыми распространенными ошибками, встречающимися в сценариях, и приемами поиска и устранения неисправностей.
Синтаксические ошибки
Один из самых распространенных видов ошибок — синтаксические ошибки. Синтаксические ошибки возникают при неправильном вводе некоторого элемента с нарушением синтаксиса командной оболочки. Чаще всего эти ошибки вызывают отказ командной оболочки от выполнения сценария.
Для демонстрации распространенных видов ошибок в дальнейших обсуждениях мы будем использовать следующий сценарий:
#!/bin/bash
# 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 is equal to 1.
Отсутствующие кавычки
Давайте изменим сценарий, удалив кавычку в конце аргумента первой команды echo:
#!/bin/bash
# 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
./trouble: строка 10: неожиданный EOF при поиске соответствующего `"'
./trouble: строка 13: ошибка синтаксиса: неожиданный конец файла
Командная оболочка сгенерировала два сообщения об ошибках. Обратите внимание, что номера строк в сообщениях не соответствуют номеру строки, где отсутствует кавычка. Понять причину можно, мысленно последовав за программой после отсутствующей кавычки. bash продолжит поиск закрывающей кавычки и найдет ее сразу за второй командой echo. После этого командная оболочка bash очень удивится, обнаружив нарушение синтаксиса команды if, потому что инструкция fi теперь окажется внутри строки в кавычках (незакрытой).
Найти такие ошибки в длинных сценариях порой очень сложно. Хорошую помощь в этом случае может оказать текстовый редактор с подсветкой синтаксиса. Если в системе установлена полная версия редактора vim, подсветка синтаксиса в нем включается командой:
:syntax on
Отсутствующие или неожиданные лексемы
Другая частая ошибка — отсутствие закрывающего элемента в составной команде, такой как if или while. Взгляните, что получится, если убрать точку с запятой после проверки условия в команде if.
#!/bin/bash
# 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
./trouble: строка 9: ошибка синтаксиса около неожиданной лексемы `else'
./trouble: строка 9: `else'
И снова сообщение об ошибке указывает на место, расположенное гораздо дальше фактического места ошибки. Здесь складывается очень интересная ситуация. Как вы помните, if принимает список команд и проверяет код завершения последней команды в списке. В нашей программе мы задумали список с единственной командой [, которая является синонимом команды test. Команда [ принимает все, что следует за ней, как список аргументов — в данном случае четыре аргумента: $number, =, 1 и ]. В отсутствие точки с запятой в список аргументов будет добавлено слово then, что синтаксически допустимо. Следующая команда echo также допустима. Она интерпретируется как еще одна команда в списке команд, которую if должна выполнить и проверить код завершения. Далее следует неуместное здесь слово else, потому что командная оболочка распознает его как зарезервированное слово (слово, имеющее специальное значение для командной оболочки), а не как имя команды. Это объясняет смысл сообщения об ошибке.
Непредвиденная подстановка
Существуют ошибки, которые возникают лишь время от времени. Иногда сценарий работает без ошибок, а иногда терпит неудачу из-за работы механизма подстановки. Для демонстрации этой проблемы вернем точку с запятой на место и изменим значение переменной number, присвоив ей пустое значение:
#!/bin/bash
# trouble: сценарий для демонстрации распространенных видов ошибок
number=
if [ $number = 1 ]; then
echo "Number is equal to 1."
else
echo "Number is not equal to 1."
fi
При попытке выполнить сценарий после внесения изменений мы получим:
[[email protected] ~]$ trouble
./trouble: строка 7: [: =: ожидается использование унарного оператора
Number is not equal to 1.