
- •Содержание
- •Часть 1. Введение
- •Глава 1. Зачем необходимо знание языка Shell?
- •Глава 2. Для начала о Sha-Bang
- •Часть 2. Основы
- •Глава 3. Служебные символы
- •Глава 4. Переменные и параметры. Введение.
- •Глава 5. Кавычки
- •Глава 6. Завершение и код завершения
- •Глава 7. Проверка условий
- •Глава 8. Операции и смежные темы
- •Часть 3. Углубленный материал
- •Глава 9. К вопросу о переменных
- •Глава 10. Циклы и ветвления
- •Глава 11. Внутренние команды
- •Глава 12. Внешние команды, программы и утилиты
- •Глава 13. Команды системного администрирования
- •Глава 14. Подстановка команд
- •Глава 15. Арифметические подстановки
- •Глава 16. Перенаправление ввода/вывода
- •Глава 17. Встроенные документы
- •Часть 4. Материал повышенной сложности
- •Глава 18. Регулярные выражения
- •Глава 19. Подоболочки, или Subshells
- •Глава 20. Ограниченный режим командной оболочки
- •Глава 21. Подстановка процессов
- •Глава 22. Функции
- •Глава 23. Псевдонимы
- •Глава 24. Списки команд
- •Глава 25. Массивы
- •Глава 26. Файлы
- •Глава 27. /dev и /proc
- •Глава 28. /dev/zero и /dev/null
- •Глава 29. Отладка сценариев
- •Глава 30. Необязательные параметры (ключи)
- •Глава 31. Широко распространенные ошибки
- •Глава 32. Стиль программирования
- •Глава 33. Разное
- •Глава 34. Bash, версия 2
- •Глава 35. Замечания и дополнения
- •Библиография
- •Приложение B. Справочная информация
- •Приложение D. Коды завершения, имеющие предопределенный смысл
- •Приложение F. Системные каталоги
- •Приложение G. Локализация
- •Приложение H. История команд
- •Приложение I. Пример файла .bashrc
- •Приложение J. Преобразование пакетных (*.bat) файлов DOS в сценарии командной оболочки
- •Приложение K. Упражнения
- •Приложение L. Хронология
- •Приложение M. Авторские права

# |
|_______| |
|
|
| | |
| | |
||
# |
|_________| |
|
|
| | |
| | |
||
# |
|___________| |
| |
|
| | |
| | |
||
# |
| |
|
|
|
| | |
| | |
|
# .--------------------------------------------------------------. |
|||||||
# |**************************************************************| |
|||||||
# |
|
#1 |
|
|
|
#2 |
#3 |
# |
|
|
|
|
|
|
|
#=================================================================# |
|||||||
E_NOPARAM=66 |
# Сценарий запущен без параметров. |
|
|||||
E_BADPARAM=67 # Неверное число колец. |
перемещений. |
||||||
Moves= |
|
# Глобальная переменная, хранит число |
|||||
|
|
|
# Добавлено в оригинальный сценарий. |
|
|||
dohanoi() |
{ |
# Рекурсивная функция. |
|
||||
|
case |
$1 in |
|
|
|
|
|
|
0) |
|
|
|
|
|
|
|
;; |
|
|
|
|
|
|
|
*) |
|
|
|
|
$4 $3 |
|
|
dohanoi "$(($1-1))" $2 |
|
|||||
|
echo move |
$2 "-->" $3 |
# Добавлено в оригинальный сценарий. |
||||
|
|
|
let |
"Moves += 1" |
|||
|
dohanoi "$(($1-1))" $4 |
$3 $2 |
|
||||
} |
esac;; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case |
$# in |
|
|
|
|
|
|
1) |
case $(($1>0)) |
in |
# Как минимум должен быть |
хотя бы один диск. |
|||
|
1) |
|
|
|
|
|
|
|
dohanoi $1 1 3 2 |
|
|
|
|||
|
echo "Всего перемещений = $Moves" |
|
|||||
|
exit |
0; |
|
|
|
|
|
|
;; |
|
|
|
|
|
|
|
*) |
|
|
|
|
|
|
|
echo "$0: Неверное число колец"; |
|
|||||
|
exit $E_BADPARAM; |
|
|
|
|||
|
esac;; |
|
|
|
|
|
|
*) |
;; |
|
|
|
|
|
|
echo "Порядок использования: $0 N" |
|
||||||
|
|
||||||
|
echo |
" |
Где \"N\" -- это число колец." |
|
|||
|
exit $E_NOPARAM; |
|
|
|
|||
esac;; |
|
|
|
|
|
|
|
# Упражнения: |
|
|
|
|
|
||
# --------- |
исполнены дополнительные команды, если |
разместить их ниже этой строки? |
|||||
# 1) Будут ли |
|||||||
# |
Почему нет? (Это так просто!) |
|
|||||
# 2) Объясните -- как работает функция "dohanoi". |
|
||||||
# |
(А вот это уже сложнее) |
|
|
Глава 23. Псевдонимы
Псевдонимы в Bash -- это ни что иное, как "горячие клавиши", средство, позволяющее избежать набора длинных строк в командной строке. Если, к примеру, в файл ~/.bashrc вставить строку alias lm="ls -l | more", то потом вы сможете экономить свои силы и время, набирая команду lm, вместо более длинной ls -l | more. Установив alias rm="rm -i" (интерактивный режим удаления файлов), вы сможете избежать многих неприятностей, потому что сократится вероятность удаления важных файлов по неосторожности.
Псевдонимы в сценариях могут иметь весьма ограниченную область применения. Было бы здорово, если бы псевдонимы имели функциональность, присущую макроопределениям в языке C, но, к сожалению, Bash не может "разворачивать" аргументы в теле псевдонима. [54] Кроме того, попытка обратиться к псевдониму, созданному внутри "составных конструкций", таких как if/then, циклы и
288
функции, будет приводить к появлению ошибок. Практически всегда, действия, возлагаемые на
псевдоним, более эффективно могут быть выполнены с помощью функций.
Пример 23-1. Псевдонимы в сценарии
#!/bin/bash
shopt -s expand_aliases
# Эта опция должна быть включена, иначе сценарий не сможет "разворачивать" псевдонимы.
alias ll="ls -l"
# В определении псевдонима можно использовать как одиночные ('), так и двойные (") кавычки.
echo "Попытка обращения к псевдониму |
\"ll\":" |
|
ll /usr/X11R6/bin/mk* |
#* Работает. |
|
echo
directory=/usr/X11R6/bin/
prefix=mk* # Определить -- не будет ли проблем с шаблонами. echo "Переменные \"directory\" + \"prefix\" = $directory$prefix" echo
alias lll="ls -l $directory$prefix" |
|
|
|||||
echo "Попытка обращения к псевдониму \"lll\":" |
|||||||
lll |
# Список всех файлов в /usr/X11R6/bin, чьи имена начинаются с mk. |
||||||
# Псевдонимы могут работать с шаблонами. |
|
|
|||||
TRUE=1 |
|
|
|
|
|
||
echo |
|
|
|
|
|
|
|
if [ TRUE ] |
|
|
|
|
|
||
then |
|
|
|
|
|
|
|
|
alias rr="ls -l" |
|
|
|
|
|
|
|
echo "Попытка обращения к псевдониму \"rr\", созданному внутри if/then:" |
||||||
|
rr /usr/X11R6/bin/mk* |
#* В результате |
-- сообщение об ошибке! |
||||
|
# К псевдонимам, созданным внутри составных инструкций, нельзя обратиться. |
||||||
|
echo "Однако, ранее созданный псевдоним остается работоспособным:" |
||||||
fill /usr/X11R6/bin/mk* |
|
|
|
|
|||
echo |
|
|
|
|
|
|
|
count=0 |
|
|
|
|
|
||
while [ $count -lt 3 ] |
|
|
|
|
|||
doalias rrr="ls -l" |
|
|
|
|
|||
|
echo "Попытка обращения к псевдониму \"rrr\", созданному внутри цикла \"while\":" |
||||||
|
rrr |
/usr/X11R6/bin/mk* |
#* Так же возникает ошибка. |
||||
|
let |
count+=1 |
|
# alias.sh: line |
57: rrr: command not found |
||
done |
|
|
|
|
|
|
|
echo; |
echo |
|
|
|
|
|
|
alias xyz='cat $0' |
# Сценарий |
печатает |
себя |
самого. |
|||
xyz |
|
# Обратите |
внимание |
на "строгие" кавычки. |
|||
# |
Похоже работает, |
|
|
|
|
||
#+ хотя документация Bash утверждает, что такой псевдоним не должен работать. |
|||||||
# |
Steve Jacobson |
отметил, что |
|
|
|
||
# |
|
|
|
||||
#+ параметр "$0" интерпретируется непосредственно, во время объявления псевдонима. |
|||||||
exit |
0 |
|
|
|
|
|
Команда unalias удаляет псевдоним, объявленный ранее .
Пример 23-2. unalias: Объявление и удаление псевдонимов
289

#!/bin/bash |
|
|
|
|
|
|
shopt -s expand_aliases |
# Разрешить |
"разворачивание" псевдонимов. |
||||
alias llm='ls -al | more' |
|
|
|
|
||
llm |
|
|
|
|
|
|
echo |
|
|
|
|
|
|
unalias llm |
|
# Удалить псевдоним. |
|
|||
llm |
|
|
|
|
|
|
# Сообщение об ошибке, т.к. команда 'llm' больше не распознается. |
||||||
exit |
0 |
|
|
|
|
|
bash$ |
./unalias.sh |
|
|
|
|
|
total |
6 |
2 bozo |
bozo |
3072 |
Feb |
6 14:04 . |
drwxrwxr-x |
||||||
drwxr-xr-x |
40 bozo |
bozo |
2048 |
Feb |
6 14:04 .. |
|
-rwxr-xr-x |
1 bozo |
bozo |
199 |
Feb |
6 14:04 unalias.sh |
|
./unalias.sh: |
llm: command not found |
|
|
|
Глава 24. Списки команд
Средством обработки последовательности из нескольких команд служат списки: "И-списки" и "ИЛИ-списки". Они эффективно могут заменить сложную последовательность вложенных if/then или даже case.
Объединение команд в цепочки
И-список
command-1 && command-2 && command-3 && ... command-n
Каждая последующая команда, в таком списке, выполняется только тогда, когда предыдущая команда вернула код завершения true (ноль). Если какая-либо из команд возвращает false (не ноль), то исполнение списка команд в этом месте завершается, т.е. следующие далее команды не выполняются.
Пример 24-1. Проверка аргументов командной строки с помощью "И-списка"
#!/bin/bash
# "И-список"
if [ ! -z "$1" ] && echo "Аргумент #1 = $1" && [ ! -z "$2" ] && echo "Аргумент #2 = $2" |
|
then |
|
|
echo "Сценарию передано не менее 2 аргументов." |
|
# Все команды в цепочке возвращают true. |
else |
|
|
echo "Сценарию передано менее 2 аргументов." |
fi# Одна из команд в списке вернула false. |
|
# Обратите внимание: "if [ ! -z $1 ]" тоже работает, но, казалось бы эквивалентный вариант |
|
# |
if [ -n $1 ] -- нет. Однако, если добавить кавычки |
# |
if [ -n "$1" ] то все работает. Будьте внимательны! |
# Проверяемые переменные лучше всегда заключать в кавычки.
# То же самое, только без списка команд. if [ ! -z "$1" ]
then
fiecho "Аргумент #1 = $1"
if [ ! -z "$2" ] then
echo "Аргумент #2 = $2"
290
echo "Сценарию передано не менее 2 аргументов." else
fiecho "Сценарию передано менее 2 аргументов."
# Получилось менее элегантно и длиннее, чем с использованием "И-списка".
exit 0
Пример 24-2. Еще один пример проверки аргументов с помощью "И-списков"
#!/bin/bash |
|
|
ARGS=1 |
# Ожидаемое число аргументов. |
|
E_BADARGS=65 |
# Код завершения, если число аргументов меньше ожидаемого. |
|
test |
$# -ne $ARGS && echo "Порядок использования: `basename $0` $ARGS аргумент(а)(ов)" && |
|
exit |
$E_BADARGS |
|
# Если проверка первого условия возвращает true (неверное число аргументов), |
||
# то |
исполняется остальная часть строки, и сценарий завершается. |
# Строка ниже выполняется только тогда, когда проверка выше не проходит.
# обратите внимание на условие "-ne" -- "не равно" (прим. перев.) echo "Сценарию передано корректное число аргументов."
exit 0
# Проверьте код завершения сценария командой "echo $?".
Конечно же, с помощью И-списка можно присваивать переменным значения по-умолчанию.
arg1=$@ # В $arg1 записать аргументы командной строки.
[ -z "$arg1" ] && arg1=DEFAULT
# Записать DEFAULT, если аргументы командной строки отсутствуют.
ИЛИ-список
command-1 || command-2 || command-3 || ... command-n
Каждая последующая команда, в таком списке, выполняется только тогда, когда предыдущая команда вернула код завершения false (не ноль). Если какая-либо из команд возвращает true (ноль), то исполнение списка команд в этом месте завершается, т.е. следующие далее команды не выполняются. Очевидно, что "ИЛИ-списки" имеют смысл обратный, по отношению к "И-спискам"
Пример 24-3. Комбинирование "ИЛИ-списков" и "И-списков"
#!/bin/bash
#delete.sh, утилита удаления файлов.
#Порядок использования: delete имя_файла
E_BADARGS=65
if [ -z "$1" ] then
echo "Порядок использования: `basename $0` имя_файла" exit $E_BADARGS # Если не задано имя файла.
else
fifile=$1 # Запомнить имя файла.
[ ! -f "$file" ] && echo "Файл \"$file\" не найден. \
Робкий отказ удаления несуществующего файла."
# И-СПИСОК, выдать сообщение об ошибке, если файл не существует.
# Обратите внимание: выводимое сообщение продолжается во второй строке,
291