Идёт загрузка страницы...

htp://aptem.net.ru





3.3.2. cpiobr - копирование и восстановление файлов в виде потока данных


         ИМЯ: cpiobr

cpiobr Копирование и восстановление в виде потока данных командой cpio

НАЗНАЧЕНИЕ

Обеспечивает интерфейс в виде меню с командой cpio и удобства при копировании и восстановлении файлов. Выходные данные на носитель копируются в виде потока данных.

ФОРМАТ ВЫЗОВА
         cpiobr
ПРИМЕР ВЫЗОВА
cpiobr Вызывает главное меню для копирования, восстановления или выдачи списка файлов
КОМАНДНЫЙ ФАЙЛ CPIOBR
    1   :
    2   # @(#) cpiobr v1.0  Cpio stream backup and restore
     Author: Russ Sage
     
    4   if [ "$#" -gt "0" ]
    5      then echo "cpiobr: too many arguments"
    6           exit
    7   fi
     
    9   while :
    10  do
    11            c
    12            set `date`
    13            echo "
     
    15  $1, $2 $3                  $4
     
    17         Cpiobr Backup & Restore
    18         -----------------------
    19     Backup to removable media
    20     Restore from removable media
    21     List files on media
    22     Long list files on media
    23      to exit
     
    25     Press b,r,f,l or : \c"
     
    27         read CMD
    28         if [ "$CMD" = "" ]
    29           then break
    30          fi
     
    32          ABORT=off
     
    34          while :
    35          do
    36                      echo "
     
    38     Enter media type:
    39        Raw System V floppy drive (/dev/rfp021)
    40        Raw XENIX floppy drive    (/dev/rfd0)
    41        Tape drive                (/dev/rmt0)
    42        Any device                (/dev/???)
    43         to exit
     
    45        Press s,x,t,a, or : \c"
     
    47                      read MEDIA
    48                      case $MEDIA in
    49                      s|S)    DEV=/dev/rfp021
    50                              break;;
    51                      x|X)    DEV=/dev/rfd0
    52                              break;;
    53                      t|T)    DEV=/dev/rmt0
    54                              break;;
    55                      a|A)    echo "enter full pathname
     (or <> to exit): \c"
    56                              read DEV
    57                              if [ "$DEV" = "" ]
    58                                then continue
    59                                else break
    60                              fi;;
    61                     "")    ABORT=on
    62                              break;;
    63                     *)       echo "cpiobr: invalid
     command \"$MEDIA\"";;
    64                     esac
    65           done # while get media
     
    67           if [ "$ABORT" = "on" ]
    68             then continue
    69           fi
     
    71           case $CMD in
    72           b|B)   echo "\nEnter the source directory name: \c"
    73                  read SRC
    74                  cd $SRC
    75                  echo "\nPlace floppy in drive and hit  ...\c"
    76                  read CMD
    77                  find . -print | sort | cpio -ocBv > $DEV
    78                  echo "\nhit \c"
    79                  read CMD
    80                  ;;
    81           r|R)   echo "\nEnter the destination directory name: \c"
    82                  read DEST
    83                  cd $DEST
    84                  echo "\nPlace floppy in drive and hit  ...\c"
    85                  read CMD
    86                  cpio -icBvdmu < $DEV
    87                  echo "\nhit \c"
    88                  read CMD
    89                  ;;
    90           f|F)   cpio -icBt < $DEV
    91                  echo "\nhit \c"
    92                  read CMD
    93                  ;;
    94           l|L)   cpio -icBtv < $DEV
    95                  echo "\nhit \c"
    96                  read CMD
    97                  ;;
    98           *)     echo "cpiobr: invalid command \"$CMD\""
    99                  ;;
    100          esac
    101 done
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ

ABORT Флаг, определяющий, делать ли аварийное прекращение
CMD Команда, получаемая от пользователя
DEST Каталог-приемник при восстановлении
DEV Маршрутное имя устройства носителя
MEDIA Хранит тип устройства, которое будет использоваться
SRC Каталог-источник при копировании

ОПИСАНИЕ
Зачем нам нужен cpiobr?

Мы уже получили представление об удобстве и управлении копированием с помощью команды autobkp, но мы еще не имели дела с неструктурированными устройствами. Это такие устройства, которые не содержат файловую систему, а просто имеют данные, которые записаны на них в виде потока данных. В таком качестве используются магнитные ленты и иногда гибкие диски. Как указывалось ранее, у вас может отсутствовать дисковое пространство или размещенная в другом месте система для копирования в формате файловой системы. Вместо этого вам может потребоваться использовать комплект гибких дисков или магнитную ленту. Даже если у вас имеются другие возможности для копирования, может наступить время, когда копия на магнитной ленте или гибких дисках может быть оправдана как дополнительная мера предосторожности, поскольку впоследствии вы можете восстановить ленту или гибкие диски в другом месте.

Проблема заключается в том, что имеется широкий набор версий синтаксиса команды cpio для такого копирования, которые зависят от формата и используемого устройства. Если вы переключаете устройства, вы должны запомнить (или должны посмотреть) соответствующий синтаксис. Одним из решений является прямое указание различных вариаций команды cpio в тексте программы и вызов их в ответ на меню, которое просто спрашивает пользователя, какого типа носитель должен быть использован. Это наш подход при написании cpiobr. Другое преимущество системы меню заключается в том, что вы можете приспособить рутинную работу по выполнению такого рода копирования для неопытного оператора или канцелярского работника, которым требуется знать только лишь, как монтировать магнитную ленту или другой носитель и отвечать на вопросы меню.

Что делает cpiobr?

Cpiobr - это управляемая с помощью меню интерактивная утилита копирования и восстановления. На самом деле это интерфейс с командой cpio системы UNIX. Функции, предоставляемые меню, включают копирование файлов с жесткого диска на гибкий диск, восстановление файлов с гибкого диска на жесткий диск, выдачу списка имен файлов, хранимых на гибком диске и выдачу списка файлов с необязательной дополнительной информацией (подобно ls -l). Гибкий диск здесь является первичным устройством назначения, но могут использоваться и другие носители, такие как магнитная лента большой емкости или кассетная магнитная лента (streamer). После выбора типа операции, которая должна быть выполнена, нужно выбрать тип используемого устройства. Утилита Cpiobr может быть использована на устройствах системы UNIX фирмы AT&T (/dev/fp021), устройствах системы XENIX фирмы IBM (/dev/fd0), стримерной ленте (/dev/rmt0) или любом другом устройстве по вашему желанию (/dev/???). Обычно имя устройства определяет тип используемого носителя. Поскольку эта утилита предназначена для всех машин UNIX, некоторые из вариантов могут отсутствовать в вашей машине, поэтому вы имеете право выбрать любое имя устройства, которое вам необходимо.

Как только имя устройства выбрано и, если вы выполняете копирование или восстановление, вам задается вопрос, что является каталогом-источником или каталогом-приемником. Укажите имена каталогов, начиная с вашего текущего каталога или абсолютное полное имя, начиная с корня (/.), после чего cpiobr переходит в этот каталог и затем использует относительные полные имена с этого места. Тем самым исключаются любые проблемы, связанные с тем, что абсолютное полное имя становится частью самой копии. Если вы даете относительное полное имя, убедитесь в том, что оно начинается от вашего текущего каталога, чтобы cpiobr начал работать с нужного места в дереве файлов.

Когда файлы копируются на желаемый носитель, маршрутное имя, переданное cpio, начинается с "/.". Это означает, что никакого префикса имени каталога на гибком диске нет. Поэтому при восстановлении файлов обязательно нужно дать полное маршрутное имя. Все файлы, поступающие с гибкого диска, будут помещены прямо в каталог-приемник, который вы указали cpiobr.

ПРИМЕРЫ
(Здесь приводятся ответы на запросы главного меню, подменю и дополнительная информация, появляющиеся в таком порядке.)
     1.  b
         x
         $HOME
Копирует файлы на гибкий диск системы XENIX, начиная с каталога $HOME.
     2.  r
         a
         /dev/rmt0
         $HOME

Восстанавливает файлы с устройства, выбранного мной (/dev/rmt0, магнитная лента), и помещает файлы в мой регистрационный каталог.

     3.  l
         s

Выдает в широком формате информацию обо всех файлах, размещенных на гибких дисках системы UNIX машины типа PC.

ПОЯСНЕНИЯ

В строках 4-7 производится проверка на наличие ошибок условий выполнения. Единственная ошибка условий выполнения - это когда вы указали какие-либо аргументы cpiobr. Поскольку это управляемая с помощью меню утилита, никаких аргументов передавать не нужно.

Для того, чтобы получить общее представление о том, как эта утилита работает, давайте подумаем над тем, что необходимо сделать. Во-первых, мы должны определить, какое действие должно быть выполнено. Получив эту информацию, нам необходимо узнать, какое устройство должно использоваться. Что, если пользователь введет неверный выбор? Нам необходимо ожидать в цикле до тех пор, пока не будет введено правильное значение.

После получения этих двух порций информации, нам нужно определить, где искать или куда помещать файлы. После этого мы можем выполнять cpio.

Для выполнения этого сценария нам нужно всего два цикла: по одному для каждой стадии ввода. В данном случае мы используем два цикла типа "вечный цикл while". Для выхода из циклов мы используем команду командного процессора break, которая выводит нас из текущего цикла. Немного позже мы увидим наличие проблемы при таком подходе.

Основной, самый внешний управляющий цикл начинается со строки 6 и заканчивается в последней строке программы - строке 87. Целью этого внешнего цикла является управление выполнением программы в целом, получение опций меню от пользователя и окончательный выход, когда пользователь сообщает, что он закончил работу. Конечно, вы можете по-прежнему выйти из программы при помощи обычного символа прерывания, но само меню имеет ключ выхода (CR). Гораздо лучше представлять явный ключ, особенно для неопытных пользователей.

Начиная со строки 11, мы устанавливаем экран для главного меню. Командой здесь является "c", что будет пояснено позже в этой книге. Она соответствует "очистке экрана" и может быть заменена стандартной командой очистки системы UNIX, которую вы можете использовать в этом месте, если хотите.

Строка 12 устанавливает в позиционные параметры выходные данные команды date системы UNIX. Такой синтаксис достаточно редко встречается, но тем не менее очень полезен. Если бы мы не хотели делать это таким образом, мы бы должны были перехватить все выходные данные команды date в одной переменной, затем разделить их на мелкие порции и поместить каждую порцию в отдельную переменную. Это потребовало бы намного больше команд и переменных в программе. Используя наш синтаксис, мы заставляем первый позиционный параметр быть первым полем выходных данных команды date, второй позиционный параметр быть вторым полем и так далее. Для получения любого указанного поля мы используем запись вида $n, где n есть номер позиционного параметра.

Строки 13-25 - это один огромный оператор echo, который печатает главное меню. Выдача всего необходимого одним оператором echo предпочтительнее, поскольку это минимизирует накладные расходы, с которыми приходится сталкиваться при выполнении большого числа операторов. Такой путь быстрее. Если бы мы использовали оператор echo для каждой строки главного меню, то оно печаталось бы очень медленно и прерывисто. Команда UNIX cat также могла бы быть применена для этого случая, используя здесь документы (вставленный текст). В качестве примера этого может служить следующее:

        cat <<-EOF
            Main Menu Information
        EOF

Однако главная проблема возникает, когда вы печатаете приглашение. Для того, чтобы курсор ожидал ввода в конце строки приглашения, необходимо выдать на терминал символ "\c", а cat не может сделать этого. Вы выводите на экран меню с помощью cat, и echo печатает приглашение, которое направляется на другую сдвинутую строку полностью заполненного экрана. Постоянство и скорость - вот чего мы добиваемся. Обучение таким трюкам еще больше поможет вам при написании программ большого размера, которые используют множество меню и другие текстовые выводы на экран. Использование оператора 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 возвращает последний элемент в указанном маршруте, т.е. само имя файла, если маршрут содержит промежуточные каталоги.)

Строки 26-29 выводят первую часть выходного сообщения. Оператор if-then использован потому, что нам нужно менять выходное сообщение в зависимости от того, сколько символов содержит имя файла. Строка 26 определяет длину имени файла, используя команду expr. Команда expr может быть использована для сравнения двух строк и получает количество совпавших символов. Сравнение имени файла со "всеми символами" (*), таким образом возвращает длину строки. (У вас может возникнуть желание обратиться к expr(1), чтобы получить информацию о других хитростях этой многоцелевой команды.)

Это возвращаемое значение используется в операторе test для определения, содержит ли имя файла менее семи символов: возможно всего один или два символа. В последнем случае, если мы делаем табуляцию, мы получим только первую позицию табуляции. Для получения последующих табуляций мы отображаем семь символов для того, чтобы попасть на место следующего поля табуляции. (Если было 3-6 символов, мы все равно остановимся на поле второй табуляции, т.е. это место работает верно.) Затем отображаем табуляцию для того, чтобы мы попали на место окончания второй табуляции, что нам и требовалось.

Если имя файла содержит более семи символов, мы уже находимся в первой позиции табуляции или за ней. Таким образом, следующий символ табуляции передвинет нас во вторую позицию табуляции. Эффект заключается в том, что для размещения колонок не имеет значения размер имени файла (кроме случая, когда оно действительно очень длинное). Это позволяет избавиться от "блюза ползущих колонок", когда колонки сдвигаются в зависимости от размера отображаемой информации. В качестве примера такого эффекта может служить стандартная команда sum. Ее выход выглядит так:

    |    4243 3 autobkp
    |    247 1 can
    |    25167 6 cpiobr
    |    186 3 dosflp
    |    56864 2 dsum
    |    2782 1 log
    |

С другой стороны, выход dsum очень ясный и четкий, не сдвигается по всему экрану. Сдвиг делает вывод изломанным и затрудняет быстрый просмотр информации.

Чудо вывода в одну строку совершается в строке 27 (для файлов с именами менее 7 символов) или в строке 28 (для файлов с более длинными именами). Внутри команды echo в каждом случае мы прячем другие команды, но по-прежнему управляем тем, как их результаты выводятся на экран. Во-первых, сами имена файлов выводятся, будучи ранее извлеченными из полного маршрутного имени. Обратите внимание, что имена файлов не содержат информацию о том, из какого они каталога. Затем мы несколько сдвигаемся и печатаем первое поле выходной суммы для этого файла (саму контрольную сумму). Это контрольная сумма версии файла в управляющем каталоге, поскольку переменная FILE была сгенерирована для этого каталога. Команда sum выводит три значения (контрольная сумма, число блоков, занятых файлом, и само имя файла). Нам нужно получить только первое значение, которое извлекается путем выполнения команды sum и передачи ее выхода по каналу команде cut, которая и возвращает первое поле. После того, как значение контрольной суммы напечатано, мы отображаем \c для запрещения перехода на новую строку. Это сохраняет курсор в той же строке.

Здесь начинает работать строка 30. Она генерирует контрольную сумму того же файла в каталоге копии ($2 в командной строке) и текущее имя файла, вырезая только число, и печатает его правее курсора в той же строке.

Цикл завершается, когда все файлы из управляющего каталога будут проверены командой sum.

ВОЗМОЖНЫЕ МОДИФИКАЦИИ КОМАНДНОГО ФАЙЛА

Неплохой модификацией может быть вариант, чтобы dsum печатала не только значения, как это сделано, но и флаг в конце строки в случае, когда два файла отличаются. Тогда у нас не было бы необходимости просматривать непосредственно числа. Мы ищем флаг и сразу видим, когда что-то не так.

Ниже приводится решение проблемы вывода флага для того, чтобы облегчить вам работу. Оно не включено в представленную выше утилиту по той причине, что такого рода вещи могут быть выполнены относительно просто. Вы можете сами сделать эту модификацию в качестве упражнения. Текст программы, выполняющей эту работу, выглядит так:

     for FILE in $1/$FLIST
     do
             BASEF=`basename $FILE`
             S1=`sum $FILE 2>&1 | cut -d' ' -f1`
             S2=`sum $2/$BASEF 2>&1 | cut -d' ' -f1`
             if [ "$S1" = "$S2" ]
               then M=""
               else M="<---"
             fi
             if [ ` expr $BASEF : '.*'` -lt 7 ]
             then  echo "$BASEF:   \t$S1\t$S2     $M"
             else  echo "$BASEF:\t$S1\t$S2   $M"
             fi
     done

Подход к решению немного отличается от решения, принятого при написании dsum, поскольку вы не можете генерировать контрольную сумму на ходу. Вы должны перехватить выход команды sum и использовать его позже. То, что мы ищем, появляется в шестой строке. Если две контрольные суммы различны, переменная M устанавливается соответствующим образом. Если файлы различаются, переменная M получает стрелку, указывающую на то, что копия плохая.

3.4.2. log - меню доступа к файлам протокола копирования


         ИМЯ : log

log Меню доступа к файлам протокола копирования

НАЗНАЧЕНИЕ
Обеспечивает интерфейс в виде меню к файлам протокола, полученным от утилиты autobkp.
ФОРМАТ ВЫЗОВА
        log
ПРИМЕР ВЫЗОВА
        log
КОМАНДНЫЙ ФАЙЛ LOG
    1   :
    2   # @(#) log v1.0 Menu access to backup logfiles Author: Russ Sage
     
    4   c
    5   set `date`
    6   echo "
     
    8   $1, $2 $3       $4
     
    10               Logfile Menu
    11            ----------------
    12     1 - list all log file names
    13     2 - display log of home backup
    14     3 - display log of product backup
    15      to exit
     
    17     Enter command (1-3,<>): \c"
    18  read CMD
     
    20  case $CMD in
    21  "")  exit;;
    22  1)     echo "\nLogfile names:"
    23         sed -n -e "/more/s/^.*more \(.*\);;$/\1/p" $HOME/bin/log;;
    24  2)     more $HOME/bin/autobkplog.home;;
    25  3)     more$HOME/bin/auto2.bkplogm;;
    26  *)     echo "log: $CMD is not a command";;
    27  esac
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ

CMD Команда, полученная от пользователя
HOME Ваш регистрационный каталог в системе

ОПИСАНИЕ
Зачем нам нужен log?

Если вы читали эту главу, ничего не пропуская, вы уже встречались с программой autobkp. Выводные данные autobkp очень информативны и должны быть сохранены как часть операции копирования. Это еще более важно, если у вас имеются программы, запускаемые с помощью cron. Со временем некоторые из этих программ может начать работать неверно и разрушить все вами сделанные копии. Единственный способ проследить за такими вещами - это использовать файлы протокола. Файлы протокола команды cron содержат некоторую информацию, но файлы протокола программы autobkp содержат ее гораздо больше.

Проблема возникает, когда вы сталкиваетесь с наличием нескольких работ для autobkp. Вы можете не захотеть смешивать невзаимосвязанные копии файлов в одном и том же файле со списком маршрутов, поэтому вы создаете несколько файлов pathlist, несколько заданий для cron и несколько файлов протокола. Если вам нужно выполнить пять или десять подобных работ, каким образом вы проследите за всеми файлами протоколов, запомните их имена и облегчите их просмотр? Все эти проблемы решены в командном файле log.

Что делает log?

Командный файл log - это управляемая при помощи меню утилита. Меню позволяет вам видеть текущие имена файлов в файлах протокола, поэтому вам нет необходимости помнить их. Остальные команды являются входными точками в файлы протокола, использующими команду more для просмотра.

Когда мы рассматривали командный файл cpiobr, мы видели, как работает управляемая при помощи меню программа. Командный файл log несколько проще в том смысле, что он делает только один проход. Кроме того, log - это "живая" программа. Она не является статичной и должна постоянно изменяться в соответствии с вашими процедурами копирования. Посредством такой модификации log способна сообщить вам действительные имена файлов.

Log является разновидностью программы, ссылающейся сама на себя. Для показа действительных имен файлов она просматривает свое содержимое и выбирает (используя sed) имена log-файлов из команды more, которая выводит их. Поскольку вы добавляете файлы протокола, программа log может хранить текущие, потому что она просматривает сама себя для определения того, что соответствует действительности. Применяя процедуру поиска таким образом, мы избавляемся от необходимости сохранять отдельные файлы данных с именами в них или использовать какие-то соглашения об именовании выполнения той же задачи. Способность программы log обращаться самой к себе позволяет вам добавлять неограниченное число файлов протокола в список, и вам предоставляется свобода по выбору имен таких файлов.

Возможно, вы заметили, что стратегия, использованная в командном файле log, может быть использована для обеспечения вывода на экран любого набора файлов (записные книжки, документация или еще что-то). Все, что вам нужно сделать для этого - записать их в соответствии с командой more и добавить столько команд в главное меню, сколько вы хотите.

ПОЯСНЕНИЯ

Строка 4 очищает экран, используя команду c, представленную ниже в этой книге. (Вместо этого вы снова можете использовать команду clear, если она доступна.)

Строка 5 устанавливает в позиционные параметры выход команды date. Это то же самое, что мы делали в программе cpiobr. Строки 6-17 выводят меню. Здесь использован один оператор echo, как описано в cpiobr. Строка 13 читает команду пользователя.

Строки 20-27 выполняют основную работу программы. Если введенная команда была просто возвратом каретки (трактуется как нуль), программа завершается. В строке 23 команда sed просматривает файл $HOME/bin/log. Это требует, чтобы вы поместили log в подкаталоге двоичных модулей вашего регистрационного каталога. Если вы разместите ее где-либо в другом месте, вы должны изменить эту строку. Команда sed использует ключ -n, который запрещает вывод, за исключением того, что явно указано для печати. Строка -e находит имена файлов.

Данный подход использует функцию замены в команде sed. Таким образом мы можем заменить все за исключением имени файла, а затем напечатать его. Смысл этой записи примерно такой: сперва мы ищем выражение more (/more/), находя тем самым все строки в файле протокола, содержащие слово "more". По определению, каждый файл протокола выводится на экран, используя команду more. Поскольку вы добавляете файлы протокола, каждая новая строка должна содержать слово more, поэтому файлы находятся автоматически по выражению команды sed.

Затем мы указываем команде sed сделать замену. Первое выражение содержит в себе всю строку от начала до конца, но мы применяем круглые скобки для отметки внутри нее образца .*, тем самым выделяя часть строки между пробелом после "more" и первой точкой с запятой в конце строки. Если вы посмотрите на все строки в файле log, которые начинаются с "more", то вы увидите, что это соответствует имени файла, которое мы ищем.

Затем мы указываем команде sed заменить всю строку на первый образец "pattern 1". "Pattern 1" - это запись команды sed для первого отмеченного или "отмеченного биркой" выражения. Другими словами, мы заменили имя файла на всю строку целиком и указали команде sed напечатать результат, тем самым выдавая на экран имя файла.

Эта работа выполняется для такого количества операторов more, сколько вы имеете. Чем больше файлов log вы имеете, тем больше файлов обрабатывает команда sed. Обратите внимание, что оператор sed просматривает любое количество символов от начала строки для нахождения слова "more". Не указывая в программе конкретное число символов, на которое нужно отступить, вы получаете тем самым свободу выбора ваших собственных уровней отступа.

Если введенная команда не является допустимой, выдается сообщение об ошибке.

Эта программа не имеет цикла, поэтому срабатывает один раз. Если вы хотите запустить ее снова, вы должны снова ввести log.

ПРИМЕР
       $  log
       1

После запуска программы выводится меню. Введите число 1 для того, чтобы увидеть все имена log-файлов.

Теперь, когда мы изучили, как распознавать и управлять файлами вообще, давайте рассмотрим некоторые систематические методы управления ИНФОРМАЦИЕЙ в файлах. Мы начинаем в следующей главе с файлов, которые важны для нас как для программистов.

Назад | Содержание | Вперед