значения как числовые, подобно оператору number = val(STRING$) языка
Бейсик. Вы можете сказать системе, чтобы она изменила свой способ
рассмотрения символьных строк путем изменения синтаксиса операции срав-
нения. Для символьных строк сравнение выглядит так:
str1 = str2
а числовое сравнение выглядит так:
num1 -eq num2
-lt
-gt
Сверьте это с руководством. Если вы попытаетесь смешать символьное
сравнение с числовым, сравнение не будет работать. У меня забрало много
месяцев программирование на командном процессоре, пока наконец я заме-
тил это незначительное различие. Если не рассматривать подробно что-ли-
бо подобное, то такие технические ошибки кажутся неуловимыми, но можно
найти объяснения, почему что-нибудь работает не так.
Вернемся к возможности проверки кода. Если был передан ключ -c,
переменная COPY устанавливается, что значит "Да, мы собираемся копиро-
вать командой cp, а не использовать uucp". Если ключ -c не использу-
ется, переменная COPY не устанавливается.
В строках 15-16 печатается заголовочное сообщение о том, что будет
выполняться копирование. Обратите внимание, что мы спрятали команду
date системы UNIX внутри оператора echo, сократив число перехваченных
данных, которые мы должны иметь, чтобы получить дату непосредственно.
Проследите за кавычками в этом операторе. Внешние кавычки являются
двойными для того, чтобы упаковать весь аргумент для оператора echo.
Знаки ударения (`) обрамляют команду date так, что она является "выпол-
няемой внутри" и ее выходное сообщение перехватывается для наших нужд.
Одинарные кавычки внутри команды date используются для передачи форма-
та, который изменяет внешний вид значений так, чтобы заголовок выглядел
более красиво. В конце оператора echo кавычки следуют одна за другой.
Это не представляет проблемы, поскольку во вложенности нет никакой
двусмысленности. Вы должны помнить, что нужно следить за ситуацией,
когда вы и командный процессор можете расходиться во мнениях, т. е.,
когда вы должны обращаться к записи вида "\".
В строке 18 переменной SYSTEM присваивается имя удаленной системы,
в которую вы будете копировать командой uucp. Здесь она равна нулю, что
позже вызовет выполнение другой операции для обеспечения функционирова-
ния по умолчанию. Если же вы хотите всегда копировать на вполне опреде-
ленную систему, модифицируйте эту строку, чтобы назначить имя этой
системы. Если оставить строку 18 так, чтобы она назначала ноль, строка
14 поймает это значение и присвоит переменной SYSTEM имя вашей текущей
системы. Другими словами, если вы оставите строку 18 так, как она есть,
и вызовете autobkp без ключа -c, вы будете копировать командой uucp са-
ми на себя, что вполне допустимо. Однако, из соображений эффективности
вы, вероятно хотели бы выполнить autobkp -c для получения локальной ко-
пии.
Строка 19 иллюстрирует концепцию, часто используемую при програм-
мировании на командном языке. Давайте вкратце рассмотрим ее.
Первый символ - это ":". В данном случае мы интересуемся, что про-
исходит при проверке, а не возвращаемым значением, поэтому мы заставили
холостую команду ("не делать ничего") получать результат как аргумент.
Текст, следующий за двоеточием, интерпретируется так: "Если переменная
SYSTEM не установлена или установлена в ноль, присвоить ей значение,
которое следует за ней". В данном случае значение - это выход команды
uuname -l. Эта команда устанавливает, что система-приемник является той
же системой, что и исходная, если система-приемник не была прямо указа-
на ранее.
Мы используем uuname -l, а не стандартное выражение uname -n по
причине совместимости. Uname -n правильно получает имя узла из структу-
ры uts ядра операционной системы, но не все системы XENIX используют
элемент узла в виде структуры uts ядра системы. Вместо этого они посы-
лают имя в файл /etc/systemid, который соответствует микросети
(micnet), разработанной для XENIX фирмой Microsoft. Команда uuname -l -
это локальное имя (или исходная машина) для системы uucp. Эта команда
возвращает правильное значение и в UNIX, и в XENIX. Имеет смысл исполь-
зовать то, что всегда работает!
Строка 21 печатает имя исходной системы и системы-приемника. Это
сообщение добавляет информацию в запись о том, что собирается делать
autobkp, поэтому вы можете видеть по выходным данным, как вы установили
данный командный файл. Снова мы спрятали команду uuname внутри операто-
ра echo. У нас нет необходимости сохранять имя исходной системы,
поскольку оно нам всегда доступно при помощи команды uuname. Поскольку
мы всего два раза используем это имя, то решили не использовать для не-
го какую-либо переменную.
Строки 23-41 - это полный цикл, который управляет автоматическим
копированием. Управляющим циклом является оператор while, который чита-
ет значения из стандартного ввода. Заметьте, что вы можете считать
несколько значений в операторе read. Это удобно, если вы хотите читать
более одного значения, но не должны выделять каждую порцию входных дан-
ных для того, чтобы определить, является это первым, вторым или третьим
элементом данных. Мы читаем их все сразу и они присваиваются указанным
переменным. Поскольку выполняется чтение стандартного ввода, мы можем
перенаправить stdin при вызове autobkp и оператор read никогда не узна-
ет, чем они отличаются. Если мы не переназначаем входные данные, мы
должны вводить их с клавиатуры. Цикл завершается при чтении конца файла
- в данном случае конец файла со списком маршрутов или символа
control-d (^d) с клавиатуры. Поэтому управляющий цикл работает так:
"пока еще есть данные для чтения, читать их, обрабатывать, затем читать
следующие."
Строки 25-28 проверяют, является ли каталог-источник действительно
каталогом. Если нет, выдается сообщение об ошибке и оператор continue
приводит к следующей итерации цикла while.
В строке 30 производится смена каталога на каталог-источник. Вот
почему выходные данные команды find являются относительными к точке
(.). Если бы мы не выполнили команду cd, то полное имя стало бы абсо-
лютным, что могло бы отразиться на системе-приемнике. Тогда маршрут,
начинающийся с каталога-приемника, имел бы вниз от себя лишний абсолют-
ный путь.
Строка 31 печатает каталог, в котором ищутся исходные файлы. Хоро-
шо иметь их в файле протокола, поскольку вам легче будет читать и сле-
дить, где в данный момент работает autobkp.
Строки 33-40 выполняют непосредственно копирование файлов. Здесь
циклом является цикл for, который читает имена файлов из выхода команды
find. Заметьте, что это автоматически ограничивает общее число файлов,
которые может обрабатывать цикл. Этот факт ранее был объяснен в этой
книге, но давайте рассмотрим его еще раз. Если find выдает список,
состоящий из сотен файлов, то список слов оператора for переполняется и
нарушает работу вашего командного процессора (или по крайней мере ко-
манды find). Здесь принято допущение, что вы не хотите иметь так много
файлов в исходном каталоге. Вы можете избежать этого, разбивая исходный
каталог на более мелкие части и пересылая их в файл pathlist. Если вы
хотите создать действительно хороший цикл, измените его, например, так:

find . -type f -ctime 0 -name "$FILES" -print | while read FILE

Благодаря использованию такого цикла, число имен файлов теперь
можно изменить от входных ограничений для командного процессора до раз-
меров канала системы (который очень большой, практически неограничен-
ный). Изменение этой одной строки не оказывает влияния на другие части
цикла.
Давайте рассмотрим детально команду find. Во-первых, мы указали ей
поиск файлов в текущем каталоге (.). Это делает все полные имена от-
носительными по отношению к точке. Затем мы сказали команде find найти
все файлы типа f, что означает обычные файлы, а не каталоги или файлы
устройств. Мы не хотим копировать такие файлы. Дальше мы говорим ей
найти файлы, которые были изменены. Под "изменением" мы подразумеваем
доступ или модификацию. (Посмотрите в описании stat(2), какие команды
изменяют доступ, изменяют и модифицируют время. Говоря "делать поиск
для нахождения "ctime 0"", мы имеем в виду все файлы, измененные за
последние 24 часа. Объяснения, которые документация по find дает по по-
воду этих чисел, довольно непонятны, поэтому отнеситесь к ним с недове-
рием.) Затем мы говорим команде find "найти только те файлы, которые
определены путем соответствия их имен маршрутным именам, указанным в
переменной $FILES, значение которой мы читаем". В этом месте мы можем
отфильтровать файлы, которые нам не нужны (как объяснялось предвари-
тельно) или выбрать файлы, которые нам нужны. В конце мы говорим коман-
де find "напечатать имена всех файлов, которые соответствуют пере-
численным критериям". Затем имена файлов передаются в цикл for. Другими
словами, выходные данные команды find становятся аргументом для охваты-
вающего цикла for.
В строках 35-38 оператор case определяет, какого рода копирование
мы собираемся делать, и запускает команды копирования. Если переменная
COPY не установлена, мы копируем файлы командой uucp. Обратите внима-
ние, что местом назначения является SYSTEM. Если мы оставили SYSTEM в
нуле в строке 18, то SYSTEM - это наша собственная система и мы копиру-
ем командой uucp файлы к себе. Если COPY установлена, то независимо от
значения SYSTEM мы копируем (но не командой uucp) файлы в другой ката-
лог текущей системы. Этот каталог может быть на том же жестком диске
или в другой смонтированной файловой системе. После того, как файл ско-
пирован, выдается сообщение, которое говорит о том, какой файл и куда
был передан. Удобно иметь в файле протокола эту информацию, поскольку
мы имеем возможность проследить, куда были пересланы ваши скопированные
файлы.
Цикл find выполняется до тех пор, пока не скопируются все файлы в
текущем сегменте дерева. Напомним, что команда find рекурсивная, поэто-
му убедитесь, что вы указывали не больше деревьев, чем вы хотели. Если
вы указали "копировать, начиная с корня (/)", то может быть передан
каждый файл, имеющийся в системе. Когда цикл for выполнился, внешний
цикл while идет к следующей итерации. Когда все входные данные обрабо-
таны, программа завершается.

Некоторые особенности uucp

Когда используется uucp, в маршруте приемника должен быть установ-
лен бит разрешения выполнения ("x") для группы "others" (остальные) для
всех промежуточных каталогов, ведущих к файлу. Это будет выглядеть так:
--------x
Самый последний каталог должен иметь права доступа вида "wx", что-
бы uucp могла писать файл в каталог. После этого владельцем файла счи-
тается uucp. Если собственником файла хотите быть вы, скопируйте его
(используя cp, а не mv) с другим именем и он будет вашей собствен-
ностью. Если вы переименуете его командой mv, вы только измените имя,
связанное с тем же индексным описателем файла (inode). Но если вы ско-
пируете его командой cp, вы создадите новый отмеченный описатель файла.
Этот новый описатель файла (созданный вами) имеет ваши идентификатор
пользователя (uid) и идентификатор группы (gid), поэтому вы владеете
им. Если вы находитесь в корне системы и копируете файл (используя cp,
а не mv) поверх другого существующего файла, информация в описателе файла
не изменяется, а меняются только данные, доступ к которым указывает
описатель файла.
Когда uucp устанавливает предшествующие права доступа к файлу на
всех промежуточных каталогах такими, что все имеют право записи,
последний каталог НЕ будет иметь защиты. Предоставление любому пользо-
вателю права записи означает, что кто угодно может удалить или изменить
файлы в этом каталоге. Не каждый хочет давать всем это право. Если же
вы копируете файлы в обычную область команды uucp общего доступа
(/usr/spool/uucppublic/$LOGNAME), то вы должны внимательно следить за
ними. Многие системы имеют запускаемые с помощью cron программы, произ-
водящие в данном каталоге поиск файлов, к которым не было доступа в те-
чение определенного количества дней, и удаляют такие файлы - это вредит
вашим копиям. Если период хранения больше, чем промежуток между вашим
копированием, у вас может быть все в порядке. Как и многое другое, это
зависит от ваших обстоятельств и требований безопасности.

Усовершенствования

В оригинале файл со списком маршрутов имеет аргумент TYPE в конце
аргумента FROM, например /usr/russ/bin/*. Это представляет проблему
(кроме того, что показывает, что ваш автор еще не является мастером!),
потому что когда символ * будет выделен, он будет расширен в имена всех
файлов вместо того, чтобы трактоваться как литеральный символ. Простое
решение - использовать отдельные поля, что и было сделано. Мастерским
решением является экранировать метасимвол для сохранения его как лите-
рального символа. Как только символ * будет выделен из маршрутного име-
ни, символ \ представит его в виде * вместо того, чтобы дать его на
расширение. Например, можно написать так:

TYPE=`basename \"$FROM"`

Здесь символ * присваивается переменной TYPE, вместо того, чтобы
присвоить TYPE список всех файлов, которые соответствуют метасимволу.
Затем, когда будет вызвана команда find, переменная TYPE должна быть
экранирована так, чтобы метасимвол интерпретировался не командным про-
цессором, а самой командой find.

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 печатает приглашение, ко-
торое направляется на другую сдвинутую строку полностью заполненного
экрана. Постоянство и скорость - вот чего мы добиваемся. Обучение таким
трюкам еще больше поможет вам при написании программ большого размера,