Мендель Купер - Искусство программирования на языке сценариев командной оболочки
--) shift; break;;
esac
done
if [ $# -ne 0 ]; then
echo $usage
exit 65 # ==> В оригинале было "exit 2", изменено в соответствии со стандартами.
fi
if [ x${verbflag} != x ]; then
ftpflags="${ftpflags} -v"
fi
if [ x${remhost} = x ]; then
remhost=prep.ai.mit.edu
# ==> Здесь можете указать свой ftp-сервер по-умолчанию.
fi
echo quit >> ${TMPFILE}
# ==> Все команды сохранены во временном файле.
ftp ${ftpflags} ${remhost} < ${TMPFILE}
# ==> Теперь обработать пакетный файл.
rm -f ${TMPFILE}
# ==> В заключение, удалить временный файл (можно скопировать его в системный журнал).
# ==> Упражнения:
# ==> ----------
# ==> 1) Добавьте обработку ошибок.
# ==> 2) Добавьте уведомление звуковым сигналом.
Пример A-15. Указание на авторские права
Следующее соглащение об авторских правах относится к двум, включенным в книгу,
сценариям от Mark Moraes: "behead.sh" и "ftpget.sh"
/*
* Copyright University of Toronto 1988, 1989.
* Автор: Mark Moraes
*
* Автор дает право на использование этого программного обеспечения
* его изменение и рапространение со следующими ограничениями:
*
* 1. Автор и Университет Торонто не отвечают
* за последствия использования этого программного обеспечения,
* какими ужасными бы они ни были,
* даже если они вызваны ошибками в нем.
*
* 2. Указание на происхождение программного обеспечения не должно подвергаться изменениям,
* явно или по оплошности. Так как некоторые пользователи обращаются к исходным текстам,
* они обязательно должны быть включены в документацию.
*
* 3. Измененная версия должна содержать явное упоминание об этом и не должна
* выдаваться за оригинал. Так как некоторые пользователи обращаются к исходным текстам,
* они обязательно должны быть включены в документацию.
*
* 4. Это соглашение не может удаляться и/или изменяться.
*/
+
Antek Sawicki предоставил следующий сценарий, который демонстрирует операцию подстановки параметров, обсуждавшуюся в Section 9.3.
Пример A-16. password: Генератор случайного 8-ми символьного пароля
#!/bin/bash
# Для старых систем может потребоваться указать #!/bin/bash2.
#
# Генератор случайных паролей для bash 2.x
# Автор: Antek Sawicki <[email protected]>,
# который великодушно позволил использовать его в данном документе.
#
# ==> Комментарии, добавленные автором документа ==>
MATRIX="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
LENGTH="8"
# ==> 'LENGTH' можно увеличить, для генерации более длинных паролей.
while [ "${n:=1}" -le "$LENGTH" ]
# ==> Напоминаю, что ":=" -- это оператор "подстановки значения по-умолчанию".
# ==> Таким образом, если 'n' не инициализирована, то в нее заносится 1.
do
PASS="$PASS${MATRIX:$(($RANDOM%${#MATRIX})):1}"
# ==> Хитро, хитро....
# ==> Начнем с самых внутренних скобок...
# ==> ${#MATRIX} -- возвращает длину массива MATRIX.
# ==> $RANDOM%${#MATRIX} -- возвращает случайное число
# ==> в диапазоне 1 .. ДЛИНА_МАССИВА(MATRIX) - 1.
# ==> ${MATRIX:$(($RANDOM%${#MATRIX})):1}
# ==> возвращает символ из MATRIX, из случайной позиции (найденной выше).
# ==> См. подстановку параметров {var:pos:len} в Разделе 3.3.1
# ==> и примеры в этом разделе.
# ==> PASS=... -- добавление символа к строке PASS, полученной на предыдущих итерациях.
# ==> Чтобы детальнее проследить ход работы цикла, раскомментируйте следующую строку
# ==> echo "$PASS"
# ==> Вы увидите, как на каждом проходе цикла,
# ==> к строке PASS добавляется по одному символу.
let n+=1
# ==> Увеличить 'n' перед началом следующей итерации.
done
echo "$PASS" # ==> Или перенаправьте в файл, если пожелаете.
exit 0
+
James R. Van Zandt предоставил следующий сценарий, который демонстрирует применение именованных каналов, по его словам, "на самом деле -- упражнение на применение кавычек и на экранирование".
Пример A-17. fifo: Создание резервных копий с помощью именованных каналов
#!/bin/bash
# ==> Автор:James R. Van Zandt
# ==> используется с его разрешения.
# ==> Комментарии, добавленные автором документа.
HERE=`uname -n` # ==> hostname
THERE=bilbo
echo "начало создания резервной копии на $THERE, за `date +%r`"
# ==> `date +%r` возвращает время в 12-ти часовом формате, т.е. "08:08:34 PM".
# убедиться в том, что /pipe -- это действительно канал, а не простой файл
rm -rf /pipe
mkfifo /pipe # ==> Создание "именованного канала", с именем "/pipe".
# ==> 'su xyz' -- запускает команду от имени порльзователя "xyz".
# ==> 'ssh' -- вызов secure shell (вход на удаленную систему).
su xyz -c "ssh $THERE "cat >/home/xyz/backup/${HERE}-daily.tar.gz" < /pipe"&
cd /
tar -czf - bin boot dev etc home info lib man root sbin share usr var >/pipe
# ==> Именованный канал /pipe, используется для передачи данных между процессами:
# ==> 'tar/gzip' пишет в /pipe, а 'ssh' -- читает из /pipe.
# ==> В результате будет получена резервная копия всех основных каталогов.
# ==> В чем состоит преимущество именованного канала, в данной ситуации,
# ==> перед неименованным каналом "|" ?
# ==> Будет ли работать неименованный канал в данной ситуации?
exit 0
+
Stephane Chazelas предоставил следующий сценарий, который демонстрирует возможность генерации простых чисел без использования массивов.
Пример A-18. Генерация простых чисел, с использованием оператора деления по модулю (остаток от деления)
#!/bin/bash
# primes.sh: Генерация простых чисел, без использования массивов.
# Автор: Stephane Chazelas.
# Этот сценарий не использует класический алгоритм "Решето Эратосфена",
#+ вместо него используется более понятный метод проверки каждого кандидата в простые числа
#+ путем поиска делителей, с помощью оператора нахождения остатка от деления "%".
LIMIT=1000 # Простые от 2 до 1000
Primes()
{
(( n = $1 + 1 )) # Перейти к следующему числу.
shift # Следующий параметр в списке.
# echo "_n=$n i=$i_"
if (( n == LIMIT ))
then echo $*
return
fi
for i; do # "i" устанавливается в "@", предыдущее значение $n.
# echo "-n=$n i=$i-"
(( i * i > n )) && break # Оптимизация.
(( n % i )) && continue # Отсечь составное число с помощью оператора "%".
Primes $n [email protected] # Рекурсивный вызов внутри цикла.
return
done
Primes $n [email protected] $n # Рекурсивный вызов за пределами цикла.
# Последовательное накопление позиционных параметров.
# в "[email protected]" накапливаются простые числа.
}
Primes 1
exit 0
# Раскомментарьте строки 16 и 24, это поможет понять суть происходящего.
# Сравните скоростные характеристики этого сценария и сценария (ex68.sh),
# реализующего алгоритм "Решето Эратосфена".
# Упражнение: Попробуйте реализовать этот сценарий без использования рекурсии.
# Это даст некоторый выигрыш в скорости.
+
Jordi Sanfeliu дал согласие на публикацию своего сценария tree.
Пример A-19. tree: Вывод дерева каталогов
#!/bin/sh
# @(#) tree 1.1 30/11/95 by Jordi Sanfeliu
# email: [email protected]
#
# Начальная версия: 1.0 30/11/95
# Следующая версия: 1.1 24/02/97 Now, with symbolic links
# Исправления : Ian Kjos, поддержка недоступных каталогов
# email: [email protected]
#
# Tree -- средство просмотра дерева каталогов (очевидно :-) )
#
# ==> Используется в данном документе с разрешения автора сценария, Jordi Sanfeliu.
# ==> Комментарии, добавленные автором документа.
# ==> Добавлено "окавычивание" аргументов.
search () {
for dir in `echo *`
# ==> `echo *` список всех файлов в текущем каталоге, без символов перевода строки.
# ==> Тот же эффект дает for dir in *
# ==> но "dir in `echo *`" не обрабатывет файлы, чьи имена содержат пробелы.
do
if [ -d "$dir" ] ; then # ==> Если это каталог (-d)...
zz=0 # ==> Временная переменная, для сохранения уровня вложенности каталога.
while [ $zz != $deep ] # Keep track of inner nested loop.
do
echo -n "| " # ==> Показать символ вертикальной связи,
# ==> с 2 пробелами и без перевода строки.
zz=`expr $zz + 1` # ==> Нарастить zz.
done
if [ -L "$dir" ] ; then # ==> Если символическая ссылка на каталог...
echo "+---$dir" `ls -l $dir | sed 's/^.*'$dir' //'`
# ==> Показать горизонтальный соединитель и имя связянного каталога, но...
# ==> без указания даты/времени.
else
echo "+---$dir" # ==> Вывести горизонтальный соединитель...
# ==> и название каталога.
if cd "$dir" ; then # ==> Если можно войти в каталог...
deep=`expr $deep + 1` # ==> Нарастить уровень вложенности.
search # рекурсия ;-)
numdirs=`expr $numdirs + 1` # ==> Нарастить счетчик каталогов.
fi
fi
fi
done
cd .. # ==> Подняться на один уровень вверх.