Advanced Bash-Scripting Guide: Искусство программирования на языке сценариев командной оболочки | ||
---|---|---|
Назад | Вперед |
Turandot: Gli enigmi sono tre, la morte una! Caleph: No, no! Gli enigmi sono tre, una la vita! |
|
Puccini |
Использование зарезервированных слов и служебных символов в качестве имен переменных.
case=value0 # Может вызвать проблемы. 23skidoo=value1 # Тоже самое. # Имена переменных, начинающиеся с цифр, зарезервированы командной оболочкой. # Если имя переменной начинается с символа подчеркивания: _23skidoo=value1, то это не считается ошибкой. # Однако... если имя переменной состоит из единственного символа подчеркивания, то это ошибка. _=25 echo $_ # $_ -- это внутренняя переменная. xyz((!*=value2 # Вызывает серьезные проблемы.
Использование дефиса, и других зарезервированных символов, в именах переменных.
var-1=23 # Вместо такой записи используйте 'var_1'.
Использование одинаковых имен для переменных и функций. Это делает сценарий трудным для понимания.
do_something () { echo "Эта функция должна что-нибудь сделать с \"$1\"." } do_something=do_something do_something do_something # Все это будет работать правильно, но слишком уж запутанно.
Использование лишних пробелов. В отличие от других языков программирования, Bash весьма привередлив по отношению к пробелам.
var1 = 23 # Правильный вариант: 'var1=23'. # В вышеприведенной строке Bash будет трактовать "var1" как имя команды # с аргументами "=" и "23". let c = $a - $b # Правильный вариант: 'let c=$a-$b' или 'let "c = $a - $b"' if [ $a -le 5] # Правильный вариант: if [ $a -le 5 ] # if [ "$a" -le 5 ] еще лучше. # [[ $a -le 5 ]] тоже верно.
Ошибочным является предположение о том, что неинициализированные переменные содержат "ноль". Неинициализированные переменные содержат "пустое" (null) значение, а не ноль.
#!/bin/bash echo "uninitialized_var = $uninitialized_var" # uninitialized_var =
Часто программисты путают операторы сравнения = и -eq. Запомните, оператор = используется для сравнения строковых переменных, а -eq -- для сравнения целых чисел.
if [ "$a" = 273 ] # Как вы полагаете? $a -- это целое число или строка? if [ "$a" -eq 273 ] # Если $a -- целое число. # Иногда, такого рода ошибка никак себя не проявляет. # Однако... a=273.0 # Не целое число. if [ "$a" = 273 ] then echo "Равны." else echo "Не равны." fi # Не равны. # тоже самое и для a=" 273" и a="0273". # Подобные проблемы возникают при использовании "-eq" со строковыми значениями. if [ "$a" -eq 273.0 ] then echo "a = $a' fi # Исполнение сценария прерывается по ошибке. # test.sh: [: 273.0: integer expression expected
Ошибки при сравнении целых чисел и строковых значений.
#!/bin/bash # bad-op.sh number=1 while [ "$number" < 5 ] # Неверно! должно быть while [ "number" -lt 5 ] do echo -n "$number " let "number += 1" done # Этот сценарий генерирует сообщение об ошибке: # bad-op.sh: 5: No such file or directory
Иногда, в операциях проверки, с использованием квадратных скобок ([ ]), переменные необходимо брать в двойные кавычки. См. Пример 7-6, Пример 16-4 и Пример 9-6.
Иногда сценарий не в состоянии выполнить команду из-за нехватки прав доступа. Если пользователь не сможет запустить команду из командной строки, то эта команда не сможет быть запущена и из сценария. Попробуйте изменить атрибуты команды, возможно вам придется установить бит suid.
Использование символа - в качестве оператора перенаправления (каковым он не является) может приводить к неожиданным результатам.
command1 2> - | command2 # Попытка передать сообщения об ошибках команде command1 через конвейер... # ...не будет работать. command1 2>& - | command2 # Так же бессмысленно. Спасибо S.C.
Использование функциональных особенностей Bash версии 2 или выше, может привести к аварийному завершению сценария, работающему под управлением Bash версии 1.XX.
#!/bin/bash minimum_version=2 # Поскольку Chet Ramey постоянно развивает Bash, # вам может потребоваться указать другую минимально допустимую версию $minimum_version=2.XX. E_BAD_VERSION=80 if [ "$BASH_VERSION" \< "$minimum_version" ] then echo "Этот сценарий должен исполняться под управлением Bash, версии $minimum или выше." echo "Настоятельно рекомендуется обновиться." exit $E_BAD_VERSION fi ...
Использование специфических особенностей Bash может приводить к аварийному завершению сценария в Bourne shell (#!/bin/sh). Как правило, в Linux дистрибутивах, sh является псевдонимом bash, но это не всегда верно для UNIX-систем вообще.
Сценарий, в котором строки отделяются друг от друга в стиле MS-DOS (\r\n), будет завершаться аварийно, поскольку комбинация #!/bin/bash\r\n считается недопустимой. Исправить эту ошибку можно простым удалением символа \r из сценария.
#!/bin/bash echo "Начало" unix2dos $0 # Сценарий переводит символы перевода строки в формат DOS. chmod 755 $0 # Восстановление прав на запуск. # Команда 'unix2dos' удалит право на запуск из атрибутов файла. ./$0 # Попытка запустить себя самого. # Но это не сработает из-за того, что теперь строки отделяются # друг от друга в стиле DOS. echo "Конец" exit 0
Сценарий, начинающийся с #!/bin/sh, не может работать в режиме полной совместимости с Bash. Некоторые из специфических функций, присущих Bash, могут оказаться запрещенными к использованию. Сценарий, который требует полного доступа ко всем расширениям, имеющимся в Bash, должен начинаться строкой #!/bin/bash.
Сценарий не может экспортировать переменные родительскому процессу - оболочке. Здесь как в природе, потомок может унаследовать черты родителя, но не наооборот.
WHATEVER=/home/bozo export WHATEVER exit 0
bash$ echo $WHATEVER bash$Будьте уверены -- при выходе в командную строку переменная $WHATEVER останется неинициализированной.
Использование в подоболочке переменных с теми же именами, что и в родительской оболочке может не давать ожидаемого результата.
Пример 31-1. Западня в подоболочке
#!/bin/bash # Западня в подоболочке. outer_variable=внешняя_переменная echo echo "outer_variable = $outer_variable" echo ( # Запуск в подоболочке echo "внутри подоболочки outer_variable = $outer_variable" inner_variable=внутренняя_переменная # Инициализировать echo "внутри подоболочки inner_variable = $inner_variable" outer_variable=внутренняя_переменная # Как думаете? Изменит внешнюю переменную? echo "внутри подоболочки outer_variable = $outer_variable" # Выход из подоболочки ) echo echo "за пределами подоболочки inner_variable = $inner_variable" # Ничего не выводится. echo "за пределами подоболочки outer_variable = $outer_variable" # внешняя_переменная. echo exit 0
Передача вывода от echo по конвейеру команде read может давать неожиданные результаты. В этом сценарии, команда read действует так, как будто бы она была запущена в подоболочке. Вместо нее лучше использовать команду set (см. Пример 11-14).
Пример 31-2. Передача вывода от команды echo команде read, по конвейеру
#!/bin/bash # badread.sh: # Попытка использования 'echo' и 'read' #+ для записи значений в переменные. a=aaa b=bbb c=ccc echo "один два три" | read a b c # Попытка записать значения в переменные a, b и c. echo echo "a = $a" # a = aaa echo "b = $b" # b = bbb echo "c = $c" # c = ccc # Присваивания не произошло. # ------------------------------ # Альтернативный вариант. var=`echo "один два три"` set -- $var a=$1; b=$2; c=$3 echo "-------" echo "a = $a" # a = один echo "b = $b" # b = два echo "c = $c" # c = три # На этот раз все в порядке. # ------------------------------ # Обратите внимание: в подоболочке 'read', для первого варианта, переменные присваиваются нормально. # Но только в подоболочке. a=aaa # Все сначала. b=bbb c=ccc echo; echo echo "один два три" | ( read a b c; echo "Внутри подоболочки: "; echo "a = $a"; echo "b = $b"; echo "c = $c" ) # a = один # b = два # c = три echo "-------" echo "Снаружи: " echo "a = $a" # a = aaa echo "b = $b" # b = bbb echo "c = $c" # c = ccc echo exit 0
Огромный риск, для безопасности системы, представляет использование в скриптах команд, с установленным битом "suid". [1]
Использование сценариев в качестве CGI-приложений может приводить к серьезным проблемам из-за отсутствия контроля типов переменных. Более того, они легко могут быть заменены взломщиком на его собственные сценарии.
Bash не совсем корректно обрабатывает строки, содержащие двойной слэш (//).
Сценарии на языке Bash, созданные для Linux или BSD систем, могут потребовать доработки, перед тем как они смогут быть запущены в коммерческой версии UNIX. Такие сценарии, как правило, используют GNU-версии команд и утилит, которые имеют лучшую функциональность, нежели их аналоги в UNIX. Это особенно справедливо для таких утилит обработки текста, как tr.
Danger is near thee -- Beware, beware, beware, beware. Many brave hearts are asleep in the deep. So beware -- Beware. |
|
A.J. Lamb and H.W. Petrie |
[1] |
Установка этого бита на файлы сценариев не имеет никакого эффекта. |