которые используют множество меню и другие текстовые выводы на экран.
Использование оператора echo в таком виде имеет некоторые не-
достатки, но они совершенно тривиальные. Во-первых, тело оператора echo
должно содержать все, что вы хотите вывести на экран, и это требует
абсолютного позиционирования внутри оператора echo для получения симво-
лов пробела в нужных местах. Обычно при таком позиционировании имеется
сдвиг строк в тексте программы, поэтому визуально в том месте командно-
го файла, где выводится меню, появляется этот сдвиг, но после меню
строки снова идут ровно. Это может немного смущать при чтении текста
программы.
Другой несущественной деталью является то, что оператор echo не
любит выводить символы табуляции. Если же вы вставили символ табуляции
внутри кавычек оператора echo, он обычно выводится как пробел. Для то-
го, чтобы заставить echo выводить символы табуляции, вы должны сказать
это оператору echo на его собственном языке с помощью символов "\t" или
\\t, если без кавычек. Поэтому меню в cpiobr заполнено символами пробе-
ла. Это также позволяет легко сдвигать меню влево и вправо при помощи
небольшого количества пробелов для соответствующего позиционирования на
экране.
Одним из спорных вопросов является место, где меню должны поя-
виться на экране. Глобальное выравнивание по левому краю выглядит
ужасно, но центрирование нарушается при выдаче на экран какого-либо
сообщения (например, от cpio). В данном случае сделано выравнивание не
по центру, а по левому краю. Неплохим компромиссом может быть отступ на
три-пять позиций от левого края. Как и в большинстве случаев, когда де-
ло идет об эстетике, вы можете с этим не согласиться.
Вернемся к нашему меню. Для того, чтобы сделать меню на экране и
более эстетичным, и информативным, на экран выводятся дата и время.
(Заметьте, что главное меню очищается каждый раз перед его использова-
нием.) Пример вида экрана приведен ниже.
---------------------------------------
| Среда, май 28 13:18:49
|
| Cpio - Сохранение/восстановление файлов
| ---------------------
| Копирование данных
| Восстановление данных
| Список файлов на носителе
| Полный список файлов на носителе
| <ВК> для выхода
|
| Нажмите b,r,f,l, или <ВК>:

В левом верхнем углу расположен день недели, месяц, день месяца.
Это поля 1, 2 и 3 команды date. В правом верхнем углу расположено теку-
щее время. Это поле 4 команды date. Все эти данные приводятся для того,
чтобы меню на экране смотрелось красиво, было равномерно заполнено и
информативно.
После того, как меню выдано на экран, строка 27 читает команду
пользователя. Заметим, что один из ключей вызывает завершение програм-
мы, если был нажат только возврат каретки. Каким образом мы проверяем
это? Мы заключаем в кавычки входную переменную таким образом, что про-
верка распознает нулевое значение (см. строки 28-30). Если был введен
ноль, мы выходим из текущего цикла while. Тем самым мы попадаем в конец
программы, которая после этого завершает выполнение. Если входное зна-
чение не было равно нулю, мы продолжаем и выполняем в следующей команде
проверку на наличие ошибки.
В строке 32 проводится инициализация переменной ABORT путем
сбрасывания ее. Это будет детально пояснено позже. А сейчас мы только
скажем, что эта переменная существует, поскольку имеется конфликт между
тем, как выполняется команда break, и структурой данной программы (т.е.
полностью управляемой с помощью меню утилиты).
В строках 34-65 разместился вложенный цикл while, который обраба-
тывает подменю. Причина, по которой мы использовали циклы типа "вечный
while" заключается в том, что они выполняются, пока не получат нужное
входное значение. Как только получены правильные входные данные, мы вы-
ходим из цикла. Это очень простой способ обработки меню.
В строках 36-45 мы снова используем оператор echo для выдачи на
экран полного подменю. Это меню запрашивает имя устройства, которое
используется в командах копирования/восстановления.
Строка 47 читает входные данные от пользователя в переменную
MEDIA. Значение переменной MEDIA затем оценивается в операторе case.
Обратите внимание, что шаблоны сравнения включают символы и в верхнем,
и в нижнем регистре. Это облегчает жизнь пользователя и делает немного
более логичным программирование, уменьшая число проверок на ошибки, ко-
торое мы должны произвести. Также заметьте, что каждый образец заканчи-
вается оператором break. Когда обнаружено допустимое входное значение,
мы желаем продолжать выполнение после конца оператора while, что осу-
ществляется оператором break. Переменная DEV теперь установлена как
маршрут к выбранному устройству.
Ключ "a" в строках 55-60 требует дальнейшей обработки. Пользова-
тель запрашивается об имени устройства, которое он выбрал. Если пользо-
ватель забыл имя или решил не использовать этот ключ, он может ввести
возврат каретки, который распознается как нуль и приводит к выполнению
оператора continue. Это вызывает выполнение следующей итерации текущего
цикла, которая снова выводит подменю. Еще один возврат каретки после
этого может использоваться для выхода из подменю и возврата в главное
меню. В противном случае, если пользователь ввел ненулевое значение,
выполняется оператор break, и цикл меню завершается, имея маршрут, ука-
занный в переменной DEV.
Если пользователь ввел неверное значение, печатается сообщение об
ошибке и подменю выводится снова. Заметьте, что ввод только возврата
каретки в подменю устанавливает переменную ABORT в "on". Почему это
так? Теперь мы подошли к той части, где язык командного процессора неп-
рименим для нашего случая. Сценарий выглядит примерно так. Мы находимся
в подменю. Мы решаем, что не будем здесь производить выбор, поэтому мы
хотим выйти из подменю и вернуться в главное меню (или в предыдущее ме-
ню). Если мы выйдем из цикла while подменю, мы попадем во внешний цикл
while и продолжим обработку запросами о каталоге-источнике и катало-
ге-приемнике.
Если мы попытаемся решить эту проблему путем использования опера-
тора "break 2", мы выйдем из обоих циклов while (попадая в самый низ
программы) и программа завершится, ничего не сделав для нас. Снова не
то, что мы хотим. Что мы действительно хотим, так это выйти из текущего
(внутреннего) цикла и продолжить следующую итерацию во внешнем цикле
для получения главного меню. Нет никакой возможности сказать командному
процессору об этом, поэтому мы создали переменную в качестве флага для
имитации этого действия и назвали ее ABORT. Если мы устанавливаем пере-
менную ABORT в состояние "да", то мы НЕ желаем продолжать работу с ко-
мандой главного меню, а хотим прекратить ее и вернуться в главное меню.
На самом деле это означает продолжить, поэтому в строках 67-69 проверя-
ется именно это. Если флаг ABORT установлен, подменю принудительно за-
вершается и оператор continue заставляет снова печатать главное меню
вместо того, чтобы пытаться выполнить какую-то наполовину определенную
операцию копирования.
В строке 71 мы получаем для проверки команду главного меню и ин-
формацию, необходимую для ее обработки. Четырьмя основными командами
являются копирование, восстановление, выдача списка файлов и выдача
списка файлов с полной информацией. Если введена какая-то другая коман-
да, выдается сообщение об ошибке и главное меню снова выводится на эк-
ран. Единственный способ выхода из главного меню - это нажать возврат
каретки без какого либо-текста перед ним и строка 28 позволит выйти из
цикла.
Команды копирования и восстановления используют относительное име-
нование. Сначала они требуют указать каталог, затем переходят в этот
каталог. Оператор echo и "холостое" чтение переменной CMD просят поль-
зователя вставить дискету (или смонтировать магнитную ленту или еще
что-нибудь) и нажать возврат каретки, когда все готово.
В случае копирования для поиска ВСЕХ файлов, размещенных в дереве
файлов, начиная с текущего каталога, используется команда find. Опера-
тор find выдает отсортированный список файлов, поэтому файлы на носите-
ле с копией отсортированы. Затем отсортированный список файлов переда-
ется по каналу команде cpio с опциями -ocBv. Это означает: "потоковый
вывод, использовать символьные заголовки, блоки размером по 5K, с выда-
чей сообщений". При этом печатаются имена файлов по мере того, как они
копируются на носитель.
В случае операции восстановления используются ключи -icBvdmu. Это
значит "потоковый ввод, использовать символьные заголовки, блоки по 5К,
с выдачей сообщений для печати имен файлов по мере их восстановления,
создавать каталоги при необходимости, файлы сохраняют исходную дату мо-
дификации, и все файлы безусловно копируются".
Если должен быть выдан только список файлов, то ключами будут или
-icBt для печати таблицы скопированных файлов (это соответствует записи
команды cpio "ls"), или -icBtv для печати таблицы файлов с более под-
робной информацией ("ls -l" в записи для cpio).
В конце выполнения каждой команды главного меню выдается сообщение
hit (Нажмите <ВК>)
Это сделано по той причине, что когда печатается главное меню, оно
очищает экран. Если бы вы хотели получить список файлов, как описано
выше, и не завершить работу, то напечатался бы список, выполнение
достигло бы внешнего оператора "done", внешний цикл снова стартовал бы
и экран очистился бы перед новой выдачей на него главного меню. Уф, на-
конец появился список, даже до того как Эвелин Вуд с высшим образовани-
ем смогла прочитать это! Для того, чтобы задержать очистку экрана, мы
ожидаем нажатие на клавишу. Что бы ни было введено, оно читается в пе-
ременную и снова никогда не используется. Это просто холостая перемен-
ная.

Замечания по операции копирования

Вы можете производить копирование файлов многими путями, но давай-
те рассмотрим некоторые отличия между командами cpio и tar. Первона-
чально командой копирования в UNIX была команда tar. Эта утилита созда-
ния копии на магнитной ленте была предназначена для ведения архивов на
магнитной ленте и выполнения самого копирования. Она работает, но имеет
некоторые особенности. Во-первых, каждый файл, помещаемый на ленту (или
какой-либо носитель, который вы используете), выравнивается на границу
килобайта. Это означает, что если ваш файл состоит из одного байта, ко-
манда tar выделяет минимум 1К вашему файлу, что может привести к значи-
тельной растрате пространства, если у вас много небольших файлов и вы
копируете их на магнитную ленту. Однако команда tar имеет также ряд
неплохих аспектов.
Например, вы можете сказать ей, какой множитель блокировки вы
используете и насколько велик образ копии, так что вы можете разбить
большие копируемые потоки данных на много мелких частей (например k=360
для гибких дисков низкой плотности в системе XENIX). Одним из странных
аспектов команды tar является то, что копия является одним длинным и
непрерывным потоком, а при восстановлении используется уникальный фор-
мат для каждого носителя. Например, вы должны, скажем, скопировать 10M
данных. Вам лучше иметь достаточно отформатированных гибких дисков и
приготовить их до того, как вы начнете копировать командой tar. Когда
дискета заполнится, вы должны прервать выполнение команды и затем снова
продолжить. Пример такой команды мог бы выглядеть так:

cd $HOME
tar cvefbk /dev/fd048ds9 18 360 .

Здесь указано, что требуется скопировать ВСЕ файлы (рекурсивно об-
ходя дерево сверху вниз) из текущего каталога (.) в файл на указанном
устройстве, со множителем блокировки 18 K и размером образа копии 360
Кбайт. Одним из интересных аспектов здесь является то, что когда коман-
да tar рекурсивно проходит вниз по дереву, она получает имена файлов в
порядке расположения описатель файла, что обычно НЕ совпадает с отсор-
тированным порядком. Вы НЕ МОЖЕТЕ получить отсортированный список фай-
лов для копирования командой tar, если только не сделаете еще одну точ-
ную копию всех данных, которые вы хотите скопировать и не разместите
описатель файла в отсортированном порядке. Как это сделать? Вот так:

cd $HOME
find . -print | sort | cpio -pdv /bkpsort

При этом получится новая копия всех ваших данных и имена файлов
разместятся в отсортированном порядке. Если после этого вы перейдете в
каталог /bkpsort и выполните команду tar, файлы будут перенесены на
носитель в отсортированном порядке.
Предположим, что для выполнения копирования требуется 15 дискет.
При восстановлении этого полного набора вы должны вводить приведенную
ниже команду 15 раз, поскольку на каждом из гибких дисков располагается
уникальный образ копии.

tar xvf /dev/fd048ds9

Такие повторные вводы команды раздражают, но они могут облегчить
жизнь, что мы вскоре и увидим.
Команда cpio является следующим поколением команд копирования. Ее
общие функции подобны функциям команды tar, но имеется несколько важных
отличий. Во-первых, вы должны сгенерировать список файлов для cpio, что
означает использование команды find для порождения списка. Поскольку мы
можем конвейером пропустить список, полученный от команды find, через
команду sort, нам нет необходимости делать еще одну копию всех наших
файлов для получения отсортированного списка.
Далее, команда cpio не выполняет выравнивание границу килобайта.
Она пакует все данные непрерывно и имеет магическое число в заголовке
для указания начала каждого нового файла. В команде cpio также нет ука-
зания размера образа на носителе. Как она это узнает? Драйвер уст-
ройства должен определить размер и послать соответствующий сигнал об-
ратно команде cpio, которая после этого приостанавливается и предлагает
вставить следующую дискету. Это все прекрасно до тех пор, пока вы не
попадаете в систему, в которой драйверы ужасны, как в системе XENIX.
Драйверы XENIX не распознают, когда нужно остановиться и продолжают вы-
полнять работу и после того, как достигнут конец гибкого диска. Прог-
рамма cpio должна была бы выполняться одинаково на всех системах, но
она не работает корректно на машинах с системой XENIX.
Одно существенное различие между командами cpio и tar заключается
в получающемся образе копии. Поскольку cpio не различает границ между
различными носителями (дискетами), файлы получаются разорванными между
двумя гибкими дисками. Это значит, что когда вы пытаетесь копировать с
этих 15 дискет, то они являются ОДНИМ непрерывным потоком входных дан-
ных, точно так, как и последовательный поток выходных данных при созда-
нии этой копии. Что произойдет, если дискета номер 2 повредится? ВСЕ
ваши файлы после второй дискеты стали бесполезны. Вы просто потеряли
весь ваш образ копии. Поскольку tar копирует в виде отдельных образов,
когда дискета номер 2 потеряется, вы все равно можете копировать с
дискет 3-15 без проблем.
Еще один прекрасный аспект команды cpio заключается в том, что она
может работать как в формате файловой системы, так и в потоковом форма-
те. Формат файловой системы (опция -p) обращается к блочным уст-
ройствам, таким как жесткий диск, а потоковый формат обращается к нест-
руктурированным устройствам (опции -i и -o), таким как магнитная лента
или гибкий диск с форматом низкого уровня. Cpio - это прекрасная утили-
та для использования ее при копировании файловых деревьев системы на
жестком диске.
Как же управляется с этим система 4.2 BSD? В течение многих лет
применялась команда tar для пересылки туда и обратно, как описано на
страницах руководства по tar(1). Не самый элегантный подход, но рабо-
тоспособный. Сейчас они имеют ключ -r (для рекурсивного обхода дерева
сверху вниз) для обычной команды cp. Я же по-прежнему считаю, что ко-
манда cpio лучше.

3.4. Средства проверки операций копирования
3.4.1. dsum - контрольные суммы двух катологов

-------------------------------------------------------------
Имя: dsum
_____________________________________________________________
dsum Контрольная сумма двух каталогов

    НАЗНАЧЕНИЕ


Выдает на экран выходные данные команды sum системы UNIX для двух
копий файлов из двух разных каталогов в одной строке. Это позволяет
быстро визуально оценить, одинаково ли содержание файлов и может быть
использовано для проверки копии.

    ФОРМАТ ВЫЗОВА



dsum [-c|-o] control_dir backup_dir

Пример вызова
dsum $HOME/bin /mnt
Просматривает, были ли какие-либо файлы изменены при копировании
из моего регистрационного каталога на гибкий диск, смонтированный в ка-
талоге /mnt.

Командный файл dsum

1 :
2 # @(#) dsum v1.0 Dual directory sum Author: Russ Sage

4 if [ $# -lt 2 -o $# -gt 3 ]
5 then echo "dsum: invalid argument count" >&2
6 echo "usage: dsum [-c|-o] control_dir backup_dir" >&2
7 echo " -c = C source files, -o = object files" >&2
8 exit 1
9 fi

11 case $# in
12 2) FLIST=*;;
13 3) case $1 in
14 -c) FLIST=*.c;;
15 -o) FLIST=*.o;;
16 *) echo "dsum: invalid argument $1" >&2
17 echo "usage: dsum [-c|-o] control_dir
bacup_dir" >&2
18 exit 1;;
19 esac
20 shift;;
21 esac

23 for FILE in $1/$FLIST
24 do
25 BASEF=`basename $FILE`
26 if [ `expr $BASEF : '.*'` -lt 7 ]
27 then echo "`$BASEF: \t'`sum $FILE | cut -d' '
-f1`\t\c"
28 else echo "$BASEF:\t`sum $FILE | cut -d' '
-f1`\t\c"
29 fi
30 sum $2/$BASEF | cut -d' ' -f1
31 done

Переменные среды выполнения

BASEF Содержит базовое имя файла из полного маршрутного
имени
FILE Содержит имя каждого проверяемого файла
FLIST Содержит указание на тип проверяемых файлов

Описание
Зачем нам нужен dsum?

В среде разработки программ всегда имеется масса файлов. Эти файлы
содержат все: исходный код, перемещаемые модули, объектный код, данные,
тексты. Другим аспектом среды разработки программ является то, что эти
файлы обычно рассыпаны по многим различным машинам (или группам машин,
может быть и такой случай). В этом случае всегда кажется, что имеется
очень много перемещений файлов: эти файлы передаются из одной системы
на другую, некоторые модифицируются, пересылаются обратно и так далее.
Похоже на то, как в армии роют ямы: вы делаете это, потому что вам при-
казано.
Когда вы перемещаете много файлов, то какой путь является лучшим
для того, чтобы гарантировать себе (или кому-либо еще), что выполненная
вами копия является ТОЧНО такой, как и оригинал? Если вы внесли ошибку
в первоначальную копию, затем распространили эту ошибку на многие копии
или даже записали вместо оригинала модифицированную копию, то вы можете
никогда не вернуться в первоначальное состояние.
Одним из способов слежения за копиями является использование ко-
манды sum. Эта команда читает данные и выводит число, являющееся разно-
видностью контрольной суммы. Другими утилитами UNIX, которые делают
что-то подобное, являются cmp для сравнения объектных файлов и diff для
обнаружения различий в текстовых файлах.
Автор привык думать, что sum будет сообщать об отличии даже в од-
ном бите (своего рода циклическая избыточная проверка), но это оказа-
лось совсем не так. Недавно имелся 35 Кбайтный файл, содержащий в виде
длинного формата список файлов, которые должны были быть скопированы. В
действительности, там были два файла, один из которых был отсортирован,
а другой нет. Они были одного размера, и sum выдала одно и то же число
для обоих файлов. Когда же cmp сравнила эти два файла, оказалось, что
39-е байты отличаются. Как мы можем объяснить тот факт, что sum
рассматривала эти два файла как совершенно одинаковые? Возможно, sum
свернула эти два файла таким образом, что контрольная сумма оказалась
одинакова, даже хотя один из файлов был отсортирован, а другой нет.
Это значит, что sum на самом деле не выполняет контрольную провер-
ку каждого бита. Только проверка алгоритма работы программы в исходном
модуле позволит убедиться в этом. Конечно, в большинстве случаев, если
файл отличается от оригинала, то это не является простой перестановкой
данных, так что sum все-таки полезна.

Что делает dsum?

Dsum - это утилита, которая выполняет проверку после копирования.
Она предполагает, что файлы скопированы из каталога-источника в ката-
лог-приемник. Каталог-источник назван управляющим каталогом, поскольку
он следит за тем, какие файлы сравниваются. Для каждого файла в управ-
ляющем каталоге печатается его имя вместе со значением его контрольной
суммы и со значением контрольной суммы для скопированного файла в ката-
логе-приемнике. Вся эта информация выдается в одной строке.
Польза от получения всей информации от dsum в одной строке заклю-
чается в том, что визуально два файла могут быть проверены очень легко.
Вам нет необходимости смотреть в другое место для получения необходимой
информации.
Альтернативой для dsum может быть выполнение какого-либо сценария,
подобного приводимому ниже.
1. Скопируйте ваши файлы в другой каталог.
2. Подсчитайте контрольную сумму всех файлов из управляющего
каталога и выведите результат в какой-либо файл.
3. Подсчитайте контрольную сумму всех файлов в каталоге,
содержащем копию, и выведите результат в какой-либо файл.
4. Сравните эти два файла командой diff для того, чтобы
увидеть, не отличаются ли какие-либо копии.
Это не дает вам даже хорошего вывода на экран о том, что происхо-
дитгиЭто не дает вам даже хорошего вывода на экран о том, что происхо-
Dsum не проходит вниз по дереву файлов, потому что большинство ко-
пий являются копиями из каталога в каталог, а не из сегмента дерева в
сегмент дерева. Из-за того, что она не выполняет такой обход, сложность
программы существенно понижается.
По умолчанию сравниваются ВСЕ файлы. Это предполагает, что вы ско-
пировали все файлы в каталог копирования. В некоторых случаях вы можете
захотеть копировать только выбранные файлы, такие как *.c (все ваши
исходные файлы). В этом случае управляющий каталог содержит множество
файлов, а каталог с копиями содержит только файлы с расширением .c.
Для поддержки таких случаев в программу включены ключи -c и -o.
Ключ -c указывает только файлы типа *.c из управляющего каталога. В ре-
зультате производится проверка только файлов *.c в каталоге с копией.
Ключ -o выполняет то же самое для файлов, соответствующих *.o.
Примеры
1. $ mount /dev/fd0 /mnt
$ cp /usr/include/* /mnt
$ dsum /usr/include /mnt
Монтирует гибкий диск в каталог /mnt. Копирует все файлы заголов-
ков в каталоге /usr/include на гибкий диск. Проверяет копии, используя
dsum для исходного каталога и для каталога с копией.

Примечание: Указывая копировать *, мы вообще не попадем
в каталог /usr/include/sys.

2. $ dsum . ..

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

Пояснения

В строках 4-9 производится проверка на наличие ошибок. Если указа-
но менее двух аргументов, значит управляющий каталог и/или каталог ко-
пии не указан и в результате обнаруживается ошибка. Если количество ар-
гументов превышает три, значит, указано еще что-то кроме ключа -c и
двух каталогов, что также является ошибкой. Все остальное (два или три
аргумента) рассматривается как допустимое значение.
В строках 11-21 производится инициализация переменной FLIST. FLIST
- это управляющая переменная, которая определяет имена файлов, на кото-
рые надо обратить внимание. Если в командной строке указаны только име-
на каталогов ($# = 2), FLIST присваивается значение по умолчанию * (все
файлы) в строке 12. Значение * присваивается переменой FLIST и не трак-
туется в это время как метасимвол (это особенность командного процессо-
ра). Если в командной строке указан ключ ($# = 3), производится провер-
ка первой переменной и FLIST получает соответствующее значение, *.c или
*.o. Если указана не такая опция, выводится сообщение об ошибке и прог-
рамма завершается.
В строках 23-31 выполняется сама работа. Здесь выполняется цикл
for, который проходит по списку слов, созданному управляющим каталогом
в соответствии со значением переменной FLIST. В строке 23 переменная
FLIST расширяется фактически с символа * в имя каждого файла. Тем самым
цикл for получает данные для использования. Следовательно, переменная
FLIST является полным маршрутным именем каждого файла в управляющем ка-
талоге.
Строка 25 разбирает расширение, сделанное в строке 19. Переменная
BASEF получает базовое имя полного маршрута из переменной FILE. Причи-
ной, по которой мы это делаем, является тот факт, что позже при ссылке
на каталог копии нам необходимо только имя файла. (В системе UNIX ко-
манда basename возвращает последний элемент в указанном маршруте, т.е.
само имя файла, если маршрут содержит промежуточные каталоги.)