Жанр: Учеба

Приемы профессиональной работы в Unix

Рас Сэйдж.
Приемы профессиональной работы в UNIX
* ВВЕДЕНИЕ *
Непрерывное снижение цен, рост производительности в наше время и
ожидаемое появление новых микро- и супер-микрокомпьютеров делают мощь
системы UNIX доступной для все большего круга пользователей. Системы
UNIX или типа UNIX работают на любых машинах, от уровня PC-XT до AT и
выше. Доступность больших объемов оперативной памяти и мощных микропроцессоров
привела к возрастанию интереса к многозадачности, системам
мультипроцессирования - сфере, в которой UNIX имеет солидную репутацию.
Однако применение UNIX с максимальной отдачей - дело нелегкое.
Люди годами высказывали неудовлетворение тем, что она не является
"дружественной" по отношению к пользователю - и это разумная критика,
хотя на самом деле UNIX содержит средства для построения интерфейсов
любого требуемого уровня сложности. Наиболее важная причина трудоемкости
эффективного использования UNIX состоит в том, что в системе
используются очень плодотворные идеи, не знакомые многим людям, работавшим
с более простыми операционными системами. UNIX предоставляет
также гораздо больше инструментальных средств, более гибких и с существенно
большими возможностями, чем, например, популярная MS-DOS (в
чем можно убедиться беглым сравнением соответствующих руководств).
Вероятно, Вы, читатель, начинали с изучения UNIX в объеме, достаточном
для решения конкретных задач в вашей системе, будь то текстовая
обработка и форматирование текстов, программирование или запуск статистических
пакетов. Через некоторое время вы, видимо, накопили (от
других людей или в результате собственной работы) небольшой набор приемов,
включающий, возможно, некоторый опыт простого программирования
для интерпретатора командного процессора.
Хотя это естественный путь развития, принимая во внимание, что в
UNIX более 200 команд, вы можете не заметить или пропустить многие
мощные и полезные идеи. Более важно то, что вы можете лишиться
перспективного взгляда, который приходит с полным пониманием того, как
работают различные части UNIX, и концепций, которые лежат в их основе.
В книге показаны многие полезные инструментальные средства и приемы,
которые вы можете сразу применять в работе, чтобы значительно повысить
производительность UNIX. В отличие от некоторых книг, которые
просто представляют набор командных файлов или других средств, здесь
описываются подробности того, как работает каждая программа, и указываются
некоторые направления адаптации программ для ваших конкретных
нужд. Сочетание инструментальных средств, концепций и техники решения
задач поможет вам стать мастером UNIX.
ЧТО ВЫ ДОЛЖНЫ УЖЕ ЗНАТЬ
Для того, чтобы извлечь пользу из данной книги, вы должны обладать
некоторым базовым опытом работы в системе UNIX. Вы должны знать
общие аспекты файловой системы, такие, как каталоги, вложенность и
маршрутные имена. Вы должны знать, как использовать один из редакторов
UNIX, чтобы вводить командные файлы интерпретатора командного процессора
и, по крайней мере, слегка знать программирование с использованием
командного процессора. Мы сделали мало допущений, касающихся
того, что вы должны знать о данной команде или особенностях UNIX. Каждая
команда или понятие объясняется, когда оно вводится, а периодические
экскурсы в ваши руководства по UNIX могут прояснить все темные
места. Запомните одно: имеется так много команд с таким большим количеством
опций, что даже мы, профессионалы, должны время от времени обращаться
к книге.
Если вы только начинаете использовать UNIX, то книга "UNIX Primer
Plus" ("Расширенный букварь по UNIX") Митчела Уэйта (Mitchell Waite),
Дональда Мартина (Donald Martin) и Стефена Прата (Stephen Prata)
(SAMS, 1983) даст вам исчерпывающее введение в предмет. Если вы уже не
новичок, но все еще не имеете четкого представления о внутренней работе
командного процессора и программировании для него, то вам даст фундаментальные
основы другая книга - "Advanced UNIX - A Programmer's
Guide" ("Руководство программиста по расширенному UNIX") Стефена Прата
(SAMS, 1985). Фактически эта книга является идеальным спутником и
справочником для дополнительных исследований, которые составляют нашу
книгу.
О КАКОЙ СИСТЕМЕ UNIX ИДЕТ РЕЧЬ
Имеется, конечно, много вариантов UNIX. Помимо основных семейств
реализаций UNIX (AT&T System V, Microsoft XENIX и Berkeley [BSD]),
распространено несколько различных командных процессоров, среди которых
наиболее широко используются два - командный процессор Bourne командный
процессор Си. Все командные файлы в данной книге были проверены
и в System V, и в XENIX с использованием командного процессора
Bourne, за исключением тех случаев, которые специально отмечены. БОЛЬШИНСТВО
наших командных файлов работает также под управлением командный
процессор Bourne в BSD, хотя нескольких команд System V нет в
системе BSD и наоборот. Мы пытались указать те места, в которых эти
две системы существенно отличаются, и дать некоторые альтернативные
подходы для пользователей BSD.
Большинство наших командных файлов было также переписано для запуска
под управлением программного процессора Си после учета синтаксических
отличий. Если вы пользуетесь командным процессором Bourne
и хотели бы поэкспериментировать с программным процессором Си, то хорошим
введением является указанная ранее книга "Advanced UNIX - A
Programmer's Guide".
Если один из наших командных файлов не работает в вашей системе,
не впадайте в панику. Проверьте, пожалуйста, следующее:
- Какая у вас версия UNIX? Отмечали ли мы что-нибудь относительно
этой версии?
- Какой командный процессор вы используете (Bourne, Си или дру-
гой)?
- Должны ли вы поменять маршрутное имя в силу того, что в вашей
системе что-то находится в другой части?
- Не утратили ли вы прав доступа к определенному файлу? Не нужно
ли вам применить команду su, чтобы получить другой идентифика-
тор пользователя или стать в корень?
- Использует ли данный командный файл предварительно под-
готовленный командный файл, который вы еще не ввели в
вашу систему?
Большинство из этих советов довольно очевидны, но никогда не мешает
сделать глубокий вдох и внимательно подумать, прежде чем нырять в
отладочные сеансы.
ОБЗОР ГЛАВ
Давайте бросим беглый взгляд на то, что описывается в данной книге,
чтобы вы получили представление о предмете книги и знали, где найти
нужную тему.

Глава 1 - введение в среду выполнения системы UNIX в целом,

способы обращения пользователей к ее различным частям. Вы увидите, каким
образом ваш рост как мастера UNIX позволит вам максимально успешно
применять все аспекты и особенности среды.
В главе 2 рассматривается наиболее важная особенность среды UNIX
- файловая система - и вводятся инструментальные средства для изучения
файловых структур и содержимого файлов.

Глава 3 предоставляет средства для практических каждодневных за-

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

Глава 6 предоставляет способы получения сведений о других пользо-

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

Глава 8 посвящена коммуникациям в UNIX - сфере, значимость кото-

рой быстро возрастает. Этот материал поможет вам работать с несогласованными
модемами, а также с проблемами безопасности и управления, которые
возникают при работе с командами cu и uucp. Предлагаемые
средства помогут вам в работе как с коммуникациями от UNIX к другой
операционной системе, так и от UNIX к UNIX. Приведены также практические
примеры аппаратных конфигураций.

Глава 9 вводит читателя в системное администрирование и безо-

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

Глава 10 завершает книгу подборкой специальных приемов UNIX,

включающих одну-две командные строки, которые действуют неожиданно эффективно.
Набор приложений предоставляет информацию, полезную при программировании
с помощью командного процессора и при отладке.
Поскольку некоторые из инструментальных средств используют командные
файлы, введенные ранее в данной книге, вы должны работать над
книгой в соответствии с последовательностью глав, когда внедряете командные
файлы в вашу систему. Однако вам не помешает сначала перелистать
всю книгу.
* ГЛАВА 1. Среда системы UNIX *
Введение
Многообразие сред системы UNIX
Ваш регистрационный каталог: как сделать его комфортным
Теории относительности a la UNIX
Жизнь системы UNIX: некоторые метафоры
СРЕДА СИСТЕМЫ UNIX
ВВЕДЕНИЕ
В данной главе рассматривается среда, которая существует в системе
UNIX и вокруг нее. Освещение всех аспектов среды UNIX было бы слишком
громоздкой задачей и выходит за пределы данной книги. Даже отдельным
утилитам, таким как fsdb и sdb, нужны свои собственные книги, чтобы
отдать им должное. Мы пытаемся дать читателю начальные сведения,
философию и ощущение системы UNIX, что лежит в основе исследований и
инструментов, представленных в этой книге.
Читая эту главу, вы, возможно, захотите прочитать (или перечитать)
команды profile(4), environ(5), term(5), termcap(5) и termio(7)
в руководствах по UNIX, чтобы ознакомиться с механизмами, которые предоставляет
UNIX для установки рабочей среды.
Внутри системы UNIX существует множество различных подсред. Все
вместе они образуют общую картину, в виде которой мы представляем себе
UNIX. Эта книга посвящена наиболее важным аспектам среды UNIX с целью
закладывания фундамента, необходимого для понимания всей системы. Это
даст вам контекст, в котором можно посмотреть на собственную работу в
системе, независимо от того, являетесь вы пользователем, программистом
или администратором системы.
В данной главе рассматриваются различные среды в компьютерах с
теоретической точки зрения, описывается "домашняя" среда и методы ее
установки, способы использования условных обозначений и глобальная
среда.
"МНОГООБРАЗИЕ СРЕД"
Каждая компьютерная система поддерживает много различных сред.
Эти среды используются как строительные блоки для создания функциональных
рабочих систем. Различные уровни необходимы как для сокращения
объема работы по управлению машиной, так и для построения такого интерфейса,
чтобы мы могли использовать компьютер на относительно высоком,
удобном для человека уровне.
Мы рассматриваем эту модель, так как она помогает выстроить в ряд
уровни, на которых мы можем работать. Имея больше знаний о том, где мы
находимся в системе, и о том, как она функционирует вокруг нас, мы можем
легче строить растущие абстрактные модели на вершине тех моделей,
которые уже имеются. Компьютеры - это фактически рабочие модели
абстракций, так что чем больше мы понимаем модели, тем лучше мы можем
использовать их для упрощения и ускорения нашей работы.
Многообразие моделей на рис.1-1 . демонстрирует различные уровни,
функционирующие внутри компьютера. Нижний слой - это стартовая точка,
от которой многообразие растет вверх. Каждый уровень строится на предыдущем
и используется для поддержки уровня, расположенного над ним.
Для каждого более высокого уровня среда более объемна и более "виртуальна"
в том смысле, что имеет место меньше условных ограничений.
Верхние уровни используют для своей работы нижние и, таким образом,
скрывают подробности, необходимые для работы этих нижних уровней. Мы
можем создать модели высокого уровня, которые работают на машине более
низкого уровня, не зная ничего о нижних уровнях.
Давайте бросим беглый взгляд на уровни модели и поговорим о том,
какими из них оперирует данная книга.
Рис. 1-1
Многообразие компьютерных сред

\ L7 / Командные файлы (scripts)
\________________________________/
\ L6 / Прикладные программы,
\____________________________/ интерпретатор команд, языковые генераторы
\ L5 / Компилятор
\________________________/
\ L4 / Операционная система
\____________________/
\ L3 / Ядро
\________________/
\ L2 / Условная машина, ассемблер
\____________/
\ L1 / Микропрограммы
\________/
\ L0 / Логические схемы, аппаратные средства
\____/

УРОВЕНЬ 0 - АППАРАТНЫЕ СРЕДСТВА
На самом нижнем уровне находятся аппаратные средства и логические
цепи. Этот уровень определяет способ хранения и обработки данных во
всех аппаратных средствах. Поскольку технология изготовления кремниевых
микросхем продолжает развиваться, этот уровень становится физически
меньше и проще, тогда как скорости запоминания и обработки продолжают
расти. На этом уровне компонентами являются центральный процессор
(ЦП), память, микросхемы поддержки и системная шина.
Отметим, что хотя прогресс на этом уровне продолжается, это вызывает
очень малые изменения на верхнем слое пирамиды. Философия системы
UNIX состоит в том, чтобы изолировать низкоуровневый аппаратный слой и
обеспечить единообразные интерфейсы к нему, которые не нуждаются в изменениях
"наверху". Верхний слой даже не должен знать о нижнем слое.
Это не значит, что события в мире аппаратуры не важны в реальном мире,
ведь противоречия реального мира влияют на скорость и емкость
ресурсов, не говоря уже об их стоимости.
УРОВЕНЬ 1 - МИКРОКОМАНДЫ
Этот уровень во многом похож на язык программирования. Он является
инструментом, который использует архитектор системы для создания
"родного" машинного языка. Машинный язык сообщает аппаратуре, какую
конкретную команду следует выполнить.
В начале эволюции ЦП большинство наборов команд были аппаратно
кодированными. Это значит, что когда ЦП получал команду, декодирование
и выполнение производилось непосредственно цепями в кремниевой микросхеме.
Благодаря прогрессу в технологии ЦП, некоторые микросхемы могут
быть программируемыми на уровне исполнения команд, что позволяет
конструкторам создавать и реализовывать новые наборы команд с минимальными
усилиями.
УРОВЕНЬ 2 - УСЛОВНАЯ МАШИНА
Данный уровень обеспечивает трансляцию из мнемоник языка ассемблера
в коды операций и данные машинного языка. Язык ассемблера - это
некоторая англо-подобная нотация, которая облегчает человеку понимание
и управление работой компьютеров.
Условная машина поддерживается ассемблером. Ассемблер может превращать
идеи более высокого уровня в цепочки чисел, которые могут быть
затем выполнены. Наряду с ассемблером, применяются модели, помогающие
использовать аппаратуру компьютера. Здесь мы можем определить такие
вещи, как стеки, вектора прерываний и периферийный ввод-вывод.
УРОВЕНЬ 3 - ЯДРО
Ядро является следующим логическим продвижением вверх и концепцией,
которую можно теперь реализовать программно на условной машине.
Ядро предоставляет среду, поддерживающую еще большие абстракции, чем
те, что рассматривались до сих пор. Двумя наиболее важными абстракциями
на уровне ядра являются управление процессами для мультипрограммирования
и многозадачности, и файловая система, которая управляет хранением,
форматом, поиском файлов и т.п. Когда эти две области переплетаются,
мы имеем базовую функцию многопользовательской машины и ядро
операционной системы.
Одной из наиболее важных областей, которыми управляет ядро, является
безопасность. Проверки идентификации пользователя выполняются в
системных вызовах внутри ядра. Определенные механизмы используются ядром
для управления безопасностью файлов, устройств, памяти и процессов.
Единственный способ отключить механизмы безопасности состоит
в изменении исходного кода ядра и перекомпиляции всей системы, что
крайне нежелательно.
УРОВЕНЬ 4 - ОПЕРАЦИОННАЯ СИСТЕМА
Данный уровень строится на ядре, чтобы создать полную операционную
среду. Потребность в дополнительных функциях системы можно удовлетворить
созданием автономных программ, имеющих конкретное назначение.
Таким образом, совокупность всех специфических функций определяет
операционную систему.
УРОВЕНЬ 5 - КОМПИЛЯТОРЫ
Компилятор - это инструмент (или программа), построенный на операционной
системе для дальнейшей разработки более совершенных и более
мощных сред. Новые среды могут предполагать еще большие абстракции,
чем на нижнем уровне, и делать больше допущений о том, что уже существует.
Это делает возможным символические конструкции более высокого
уровня, такие как структуры данных и управляющие структуры. Результатом
является прикладная программа.
С помощью компилятора мы можем определить совершенно новый язык и
сделать его рабочим на компьютере, написав компилирующую программу,
которая читает этот новый язык. Это открывает целые новые области во
взаимодействии человека с машиной. Высокоуровневые языки могут воплощать
различные подходы к решению задач, например, процедурную модель
или объектно-ориентированную модель, и в конце концов, очевидно, могут
достичь выразительной мощи разговорного языка типа английского.
УРОВЕНЬ 6 - ПРИКЛАДНЫЕ ПРОГРАММЫ
В наше время прикладные программы могут означать массу разнообразных
вещей. Мы можем предположить, что любая программа, которая сделана
с помощью компилятора, является прикладной программой. Примерами
возможных прикладных программ являются следующее поколение языков, интерпретаторов
и генераторов прикладных программ. Интерпретатор - это
программа, написанная на распространенном языке высокого уровня, которая
может декодировать и исполнять другой синтаксис (или язык). Примером,
который интересует нас в системе UNIX, является командный процессор
shell. Это программа на языке Си, созданная для чтения и исполнения
команд, записанных по правилам синтаксиса, определенных командным
процессором shell.
Генератор прикладных программ - это программа, написанная на языке
высокого уровня. Она предназначена для получения достаточной информации
от пользователя о его приложении и может использовать компиляторный
язык, например Си, для написания прикладной программы, реализующей
то, что требуется. Пользователь ничего не программирует. Выходом
генератора является рабочая программа.
UNIX не делает особых различий между уровнями. Некоторые особенности
системы, например, конвейеры, являются частью ядра на нижнем
уровне. Команда типа cat выполняет довольно простую функцию на уровне
операционной системы. Нечто подобное ls напоминает простую прикладную
программу с относительно малым набором опций. Большие программы, подобные
семейству roff, определенно являются полновесными приложениями,
а средства типа sed и awk являются фактически интерпретаторами небольших
языков программирования. Замечательной особенностью системы UNIX
является единообразие, которое она вносит в этот широкий диапазон
функций.
УРОВЕНЬ 7 - КОМАНДНЫЕ ФАЙЛЫ
Этот верхний уровень является языком, который интерпретирует
программа /bin/sh (в случае командного процессора Bourne shell). Ее
синтаксис поддерживает полный язык программирования. Хотя этот язык
лишен ряда встроенных структур и функций современного языка высокого
уровня, он имеет все необходимое для написания полезных программ.
Большим плюсом является то, что языку командного процессора доступны в
качестве внешних функций любые средства, утилиты и программы, которые
имеются в системе UNIX. Это значит, что алгоритмы, которые могут потребовать
сто или более строк на языке низкого уровня типа Си, язык командного
процессора может выразить в двадцать строк. За счет потери
производительности, разумеется.
ВАШ "РЕГИСТРАЦИОННЫЙ КАТАЛОГ"
Поскольку UNIX создавалась как многопользовательская система,
многое сделано для того, чтобы система была безопасной и удобной для
каждого пользователя. Вам выделяется определенная часть файловой
системы (т.е. область на диске), которая является полностью вашей и
больше ничей. Вы можете заблокировать вашу область так, чтобы никто не
мог заглянуть вовнутрь, или же можете оставить ее открытой, чтобы другие
люди могли читать эту область или писать в нее.
Помимо определения вашего места в системе, можно привязать "домашний"
каталог (home-catalog) к вашим точным спецификациям. "Регистрационный
каталог" - это не только область файловой памяти, но и вся
ваша среда. Можно установить переменные командного языка для определения
путей по системе. Можно создать инструментарий, чтобы помочь вам в
работе.
ЧТО ТАКОЕ СОСЕДСТВО?
Во многих более старых мини- и микрокомпьютерах среда имеет
"плоскую" файловую систему. Это значит, что все файлы размещаются в
одной огромной области хранения и нет логических разделов для их разграничения.
Отсутствие разделов порождает массу файлов, через которые
нужно пробраться, когда вы хотите найти определенный элемент. Некоторые
системы имели в своих файловых системах групповые разделы, но
обычно такие разделы были различными плоскими файловыми системами.
Время показало, что такой тип среды (или модели) - не лучшее решение.
Решение, которое использует UNIX,- перевернутая модель дерева.
Корень системы находится наверху, а ветви растут в стороны и вниз.
Имеется один и только один корень наверху. Ветви могут исходить в любом
направлении и простираться вниз на любую глубину. Кроме того, вы
можете иметь присоединяемые ветви, которые можно изъять из системы, а
затем вернуть обратно. Они монтируются на существующую в системе древовидную
структуру.
Когда вы регистрируетесь в системе, вы можете попасть в любое
место древовидной структуры. Регистрационный каталог определяется в
файле паролей. К ней можно обратиться по имени $HOME, которая является
одной из предопределенных переменных командного языка для вашего
использования. Теперь у вас есть персональная древовидная структура
под этим именем каталога. Она полностью ваша и может быть сделана недоступной
для кого угодно, кроме корня. Вы можете организовать ваш регистрационный
каталог ($HOME) любым приемлемым для вас способом.
ПЛАНИРОВКА РЕГИСТРАЦИОННОГО КАТАЛОГА
Как только ваш регистрационный каталог присоединен к определенному
месту дерева, вы получаете полное управление структурой, которая
существует ниже этого места. Вы можете оставить ее плоской или сделать
подобной дереву. Эта структура зависит фактически от ваших потребностей
и энтузиазма в эксплуатации вашей собственной области. Наибольшая
выгода для нас состоит в том, чтобы использовать вашу "домашнюю"
среду для поддержки ваших работ и максимально уменьшить объем ручной
работы. В следующих двух главах описано множество средств, которые могут
работать с вашей личной файловой системой.
На рис.1-2 показана древовидная структура вашего регистрационного
каталога. Эта планировка представляет каркас среды, который вы можете
заполнить соответствующей информацией.
По мере того, как растет ваше мастерство использования системы,
вам могут понадобиться эти типы областей для размещения в них информации.
Вы обнаружите также, что наш сценарий хранения информации предполагает
движение по деревьям, или их обход, так что вам гарантируется
выгода от использования иерархической конструкции.
Давайте пройдемся по этой примерной структуре и определим, каковы
ее части. Данная структура включает много файлов и каталогов, но все
они имеют определенное назначение. Возможно, вы не захотите использовать
в точности эти имена, но вы получаете совет, какие типы категорий
могут встретиться и как использовать систему для поддержки этой структуры.
Корнем этого дерева является регистрационный каталог, который определен
в пятом поле файла /ets/passwd. Использование файла паролей
описано в passwd(4). Вот пример парольного входа автора:
russ:.pDIPADYfIXBY:103:101:Russ Sage:/usr/russ:/bin/sh
Слева направо вы видите имя пользователя (russ), пароль
(.pDI...), идентификатор пользователя (103), идентификатор группы
(101), личный комментарий, имя регистрационного каталога (/usr/russ) и
командный процессор shell, получаемый при входе в систему (/bin/ sh).
ФАЙЛЫ В РЕГИСТРАЦИОННОМ КАТАЛОГЕ
Файлы, описываемые ниже, разделяются на три категории: файлы, которые
обычно присутствуют в вашей системе, если вы работаете в System
V, файлы, которые имеются обычно в Berkeley 4.2, и файлы, которые создаются
при использовании программ из настоящей книги.
ФАЙЛЫ System V
Первый файл - это .news_time. Дата этого файла соответствует тому,
когда вы последний раз читали новости в каталоге /usr/news. Для
чтения новостей пользуйтесь командой news(1). Эта команда выдает новости,
появившиеся позже даты создания файла .news_time.
Следующий файл - .profile. Этот файл выполняется при каждой регистрации
в интерпретаторе shell и может быть использован для привязки
вашей собственной среды. В дальнейшем мы рассмотрим этот файл более
подробно.
Следующий файл - calendar (календарь). Этот файл содержит даты и
сообщения. Команда calendar(1) читает в этом файле даты, очень близкие
к текущей дате. Затем печатаются или посылаются вам по почте сообщения.
Последний файл - mbox, ваш системный почтовый ящик. Когда вы с
помощью команды mail(1) сохраняете почту, она направляется по умолчанию
в mbox.
ФАЙЛЫ 4.2 BSD
Первым файлом здесь является .cshrc. Это первая стадия настройки
системы на пользователя, выполняемой интерпретатором cshell. В системе
UNIX присутствие "rc" в имени файла означает "команды запуска" ("run
commands") или "запуск при загрузке" ("run on boot up").
Файл .login является синонимом файла .profile интерпретатора sh.
Этот файл содержит команды настройки на среду пользователя, которая
вам нужна при регистрации в системе.
Следующий файл - .logout. Он выполняется, когда вы выходите из
системы. Например, вы можете применить его для печати учетной информации,
такой как время, в течение которого вы работали в системе,
используемый вами размер дискового пространства и т.д. System V не
имеет подобного файла.
Следующий файл - .msgsrc, предназначенный для команды msgs(1)
системы Berkeley. Файл .msgsrc содержит последний, прочитанный вами
файл сообщений. Файлы сообщений хранятся в виде последовательно пронумерованных
файлов в каталоге /usr/msgs.
ТРЮКИ С ГЛАВНЫМИ ФАЙЛАМИ
Вот программы и файлы, которые вы можете разработать во время
использования данной книги. Файл .lastlog содержит даты каждого вхождения
в систему с вашими учетными данными. Программа, которая управляет
этим файлом, называется lastlog и представлена в главе 5.
Следующий файл - .trashcan. Это каталог, который временно хранит
файлы, удаленные вами. Если вы уверены, что они вам не нужны, то их
можно удалить навсегда. Эта особенность рассмотрена в главе 3.
Последний файл - .phone.list. Это ваша личная база данных со
списком телефонов. Она обслуживается командой phone (см. главу 5).
КАТАЛОГИ
Первым каталогом является adm. Он содержит административные файлы,
которые вы можете иметь, например расписания, информацию о сотрудниках,
встречах и т.д.
В каталоге bbs имеются подкаталоги для каждой "доски объявлений",
которую вы вызываете. Когда вы обращаетесь к этим системам, вы имеете
место для размещения всех соответствующих файлов и данных. Необходимая
вам информация - это меню для системы, вспомогательный текст, загрузки
программ и общая информация, которая вас интересует.
Каталог bin содержит все инструментальные средства, которые у вас
есть. Это могут быть командные файлы или объектные модули откомпилированных
программ. Подкаталог src не обязателен. В нем хранится исходный
код на языке Си для объектных модулей, имеющихся в bin, так что исходный
текст для быстрой фиксации ошибок и изменения всегда под рукой.
Каталог doc - это корень всех видов документации. Подкаталогами
здесь могут быть формы, письма, записки, разнообразная информация и
отчеты. Каждый подкаталог содержит определенные файлы в этих областях.
Каталог etc содержит любые системные или административные команды
и файлы, которыми вы пользуетесь. Если вы имеете административные обязанности,
типичным содержимым этого каталога может быть резервная копия
текущих конфигурационных файлов, используемых системой. Вы можете
сделать резервную копию файлов
/.profile
/etc/bcheckrc
brc
checklist
gettydefs
group
inittab
motd
mountable
unmountable
passwd
profile
rc
/usr/lib/crontab
/usr/lib/uucp/L.sys
USERFILE
uudemon.day
uudemon.hr
uudemon.wk
или любой другой информации о системе.
Каталог proj предназначен для специальных проектов, которые у вас
есть. Скорее всего, вы назовете этот каталог не proj, а по имени проекта,
например, dev для разработки (development) или qa для чистовой
шлифовки (quality assurance). Все данные, корреспонденция, документация
и исходный код для каждой работы направляются в главный каталог
проекта. Конечно, у вас может быть более одного каталога проекта.
Следующий каталог - mail. Это хорошее место для размещения вашей
почтовой корреспонденции от других людей, использующих систему. Имена
файлов в этом каталоге являются пользовательскими. Например, если бы я
получил почту от Боба, то она находилась бы в файле с именем bob.
Каталог src - для всего исходного кода. Логически сгруппируйте
ваш исходный код по подкаталогам, чтобы облегчить его поиск в будущем.
Возможными подкаталогами являются asm для ассемблерного кода, c для
исходного кода на Си, games (игры), misc (разное), script для командных
файлов и sys для любого исходного кода, относящегося к системе.
(Если вы держите исходные тексты ваших личных инструментов в каталоге
/bin/src, то здесь вы, возможно, продублируете их.)
Каталог sys - это склад информации, имеющей отношение к системе.
Здесь могут быть резервные копии критических системных файлов, документация
по областям системы, куски выводимой информации команд who,
ps, uucp, регистрационных файлов или что-либо иное.
Последний каталог - tmp, который является рабочей областью для
размещения временных файлов. В основном все, что находится в tmp, вы
можете в любое время удалить, и средство can, описанное в главе 3, помогает
вам в этом.
Отметим, что регистрационный каталог имеет минимальное количество
обычных файлов. Это уменьшает путаницу, которая может происходить с
плоскими файловыми системами. Каждый файл должен быть на своем месте,
но может быть размещен не только здесь. Если возникает какой-либо род
задач, когда файлы, связанные с этой задачей, могут быть перепутаны с
другими файлами, создайте отдельный каталог.
АНАЛИЗ ПРИМЕРА ФАЙЛА НАСТРОЙКИ СИСТЕМЫ
Файл .profile, как следует из его названия (профилирование - формирование
контура, очертаний), функционирует для установки и инициализации
параметров системы, которые вам нужны. Сюда входит установка
терминалов, определение переменных, запуск программ и конфигурирование
исполняющей системы. Пример файла настройки можно найти в описании команды
profile(4). Рассмотрим .profile, используемый автором. Мы включили
его на столь ранней стадии, чтобы предложить вам коснуться понятий,
которые более подробно раскрываются далее в этой книге. Не заботьтесь
о понимании работы каждой части примера. Пока что просто сделайте
мысленную отметку возможностей. Ваш .profile может быть проще и
почти наверняка будет другим.
1 # @(#).profile v1.0 Defines "home" on the system
Author: Russ Sage
3 CHOICE="ushort"
4 case $CHOICE in
5 ufull) PS1="`uuname -l`" ";;
6 ushort) PS1="`uuname -l|cut -c1-3`" ";;
7 graphic) PS1="^[[12mj^[[10m ";;
8 esac
10 LOGNAME=`logname`
11 HOME=`grep "^$LOGNAME:" /etc/passwd | cut -d: -f6`
12 MAIL=/usr/spool/mail/$LOGNAME
13 export LOGNAME HOME MAIL
15 HA=$HOME/adm
16 HBB=$HOME/bbs
17 HB=$HOME/bin
18 HD=$HOME/doc
19 HE=$HOME/etc
20 HM=$HOME/mail
21 HP=$HOME/proj
22 HSR=$HOME/src
23 HSY=$HOME/sys
24 HT=$HOME/tmp
25 HDIRS="HA HBB HB HD HE HM HP HSR HSY HT"
26 export $HDIRS HDIRS
28 P=/usr/spool/uucppublic/$LOGNAME; export P
30 CDPATH=.:..:$HOME:$HDIRS
31 PATH=.:/bin/:/usr/bin:/etc:$HOME/bin
32 SHELL=`grep "^$LOGNAME:" /etc/passwd|cut -d: -f7`
33 export CDPATH PATH SHELL
35 case "`basename \`tty\``" in
36 console) eval `tset -m ansi:ansi -m :\?ansi -r -s -Q`;;
37 tty00) eval `tset -m ansi:ansi -m :\?ansi -r -s -Q`;;
38 tty01) eval `tset -m ansi:ansi -m :\?ansi -r -s -Q`;;
38 esac
41 echo TERM = $TERM
42 TERMCAP=/etc/termcap
43 export TERM TERMCAP
45 HZ=20
46 TZ=PST8PDT
47 export HZ TZ
49 umask 0022
51 echo "\nTime of this login : `date`"
52 lastlog -l
54 RED="^[[31m"
55 GREEN="^[[32m"
56 YELLOW="^[[33m"
57 BLUE="^[[34m"
58 CYAN="^[[35m"
60 case "`date|cut -d' ' -f1`" in
61 Mon) echo "$RED";;
62 Tue) echo "$GREEN";;
63 Wed) echo "$YELLOW";;
64 Thu) echo "$BLUE";;
65 Fri) echo "$CYAN";;
66 esac
КАК РАБОТАЕТ .profile
Когда вы входите в систему, регистрационная программа выполняет
интерпретатор shell с параметром '-' (например, -sh). Это сигнализирует
интерпретатору shell, что сейчас момент регистрации и что должен
быть выполнен файл настройки. Сначала выполняется /etc/profile - общий
файл настройки, установленный системным администратором для всех пользователей,
а затем файл .profile пользователя. Каждый интерпретатор
shell после этого больше не запускает эти установочные программы. В
файле /etc/ profile интересно проверить машинно-зависимую информацию и
посмотреть, какие умолчания были для вас установлены. Если вы хотите
выполнить ваш .profile в любой момент после входа в систему, наберите
". .profile" (можно писать и ".profile", проверено, что обе формы работают
- Прим. переводчика).
Для поддержки вашего регистрационного каталога, используйте переменные
командного процессора (переменные shell), чтобы облегчить передвижение
и сократить количество нажатий клавиш при работе с маршрутными
именами. Переменные shell всегда являются строками и, будучи один
раз определенными, не исчезают, пока вы не выйдете из системы.
При использовании переменные shell являются локальными для работающего
в данный момент интерпретатора shell. Их можно передать интерпретаторам
shell более глубокого уровня путем их "экспортирования".
Следовательно, если вы создаете новый командный процессор, все ваши
экспортированные переменные будут по-прежнему определены для этого интерпретатора
shell. Исчерпывающий список переменных shell, установленных
по умолчанию и используемых системой, см. в приложении 1.
Отметим, что в нашем примере файла настройки для каждого подкаталога
первого уровня, который есть в нашем регистрационном каталоге, мы
также имеем переменные shell, связанные с именем этого подкаталога.
Таким образом, мы можем легко обращаться к различным областям нашего
регистрационного каталога.
ПОСТРОЧНЫЙ РАЗБОР ПРИМЕРА ФАЙЛА НАСТРОЙКИ
Строки 3-8 делают хитрую установку главной подсказки - переменной
PS1. В строке 3 инициализируется переменная, которая выбирает подсказку.
Значение ushort жестко закодировано в файле, но вы всегда можете
запросить его или установить его в зависимости от файла.
Первой альтернативой является ufull, используемая для установки
подсказки в виде полного имени узла uucp в локальной системе. Вы выбираете
такую подсказку, если используете несколько машин и для доступа
к одной машине применяете другую. Отличительная подсказка напоминает
вам, какой машиной вы пользуетесь. Отметим, что подсказка имеет одинаковое
число символов и для короткой строки, и для длинной. Если же вам
нужно имя узла uucp, но не нужна длинная строка для подсказки, вы можете
выбрать ushort, что дает первые три символа имени узла. Как показано
в строке 6, имя получается применением команды uuname для получения
локального имени узла (опция -l). Затем это имя пропускается через
команду cut, которая вырезает символы с первого по третий. Результат
присваивается переменной подсказки.
Последняя альтернатива для тех из вас, кто имеет графические символы.
Назначение в строке 7 есть греческий символ. Его можно получить
применением специальных управляющих последовательностей, которые указывают
терминалам отображение специальных символов. Символы ^[ являются
визуальным представлением управляющего символа в программе vi. Вы
можете получить этот символ в программе vi, набрав control-v, а затем
ESC. Последовательность ESC[12m означает, что следующий символ будет
напечатан как графический. Символ j является вашей подсказкой и превращается
в графический символ, который выдается на ваш экран. Используя
различные символы алфавита, вы можете иметь в виде вашей подсказки
почти любой графический символ. ESC[10m возвращает ваш терминал в режим
обычного текста, так что все символы, печатаемые после того, как
вы набрали ESC[10m, являются нормальными.
Если вы хотите сохранить вашу пользовательскую подсказку для всех
подчиненных интерпретаторов shell, экспортируйте ее. Иначе вы получите
$ для всех интерпретаторов shell нижнего уровня.
Строка 10 присваивает переменной LOGNAME выход команды
logname(1). Команда logname - это обычная команда системы UNIX, которая
печатает ваше регистрационное имя из файла /etc/passwd. Обычно эта
переменная установлена для вас системой, но данный пример показывает,
как вы можете установить ее вручную.
Строка 11 инициализирует переменную HOME. Она тоже устанавливается
для вас системой, но мы хотим показать, как делать эти вещи осознанно,
а не по умолчанию. Сначала мы ищем в файле паролей запись,
соответствующую переменной LOGNAME. Мы ищем от начала строки имя, которое
завершается символом :, чтобы убедиться, что найдено только корректное
соответствие имени пользователя. Затем вся запись посылается
команде cut, которая вырезает шестое поле - регистрационный каталог.
Преимущество такой стратегии в том, что регистрационный каталог автоматически
меняется, если меняется запись в файле /etc/passwd.
Строка 12 инициализирует переменную MAIL. Определяя MAIL, вы указываете,
что вы должны быть уведомлены о посылке вам новой почты, если
вы находитесь в режиме on line. Строка 13 экспортирует эти переменные,
так что они доступны нам в порожденных интерпретаторах shell.
Строки 15-24 определяют все каталоги первого уровня в нашем регистрационном
каталоге. Большинство имен состоят из двух букв, некоторые
из трех. Теперь мы можем применять команды такого вида:
$ cd $HD
$ ls -R $HSR
$ cu -ltty00 dir | tee $HBB/board/session$$
Строка 25 присваивает переменной HDIRS все имена каталоговых переменных,
что облегчает подключение всех каталогов без повторного ввода
их имен. Мы можем просмотреть все каталоги и напечатать размер
используемого дискового пространства:
$ for DIR in $HDIRS
" do
" echo "disk usage for $DIR: `du -s $DIR`"
" done
Строка 26 экспортирует переменные так, чтобы мы могли всегда их
использовать. Отметим, что мы экспортировали $HDIRS и HDIRS. Перед
тем, как выполнить экспортирование, $HDIRS было распространено на все
различные имена переменных. Следовательно, фактически мы экспортировали
все имена плюс саму переменную HDIRS.
Строка 28 инициализирует P так, чтобы это был ваш каталог в
PUBDIR, то есть /usr/spool/uucppublic. Теперь у нас есть простой
способ ссылаться на наши файлы при работе с командой uucp.
Строка 30 устанавливает CDPATH. Это путь, который проверяется,
когда вы выполняете команду cd. Сначала проверяется текущий каталог
(.) на предмет того, есть ли в нем имя каталога, в который вы хотите
попасть. Затем проверяется .. (родительский каталог). После этого
просматривается ваш регистрационный каталог. Последним назначением
CDPATH является $HDIRS, что подключает имена всех подкаталогов. Цель
этих имен - позволить команде cd искать в соответствующем каталоге
введенное вами имя.
Например, если бы вы были в /etc и набрали "cd doc", вы бы попали
в $HOME/doc, поскольку CDPATH содержало в себе $HOME. Аналогично, если
бы вы имели подкаталог $HOME/doc/status и ввели "cd status" откуда-либо
из другого места в системе, вы бы пришли в $HOME/doc/status, так
как корень $HOME/doc был в CDPATH.
Порядок поиска в каталогах такой же, как объявлено в переменной
CDPATH. Если вы вводите имя каталога, которое встречается более чем в
одном месте, вы попадаете в первый каталог, обнаруженный при последовательном
поиске. Например, если бы вы сказали "cd sys", то попали бы
в $HOME/sys прежде, чем в $HOME/ src/sys.
В табл. 1-1 приведен пример эквивалентных команд cd, представленных
в трех различных формах, которые понимает UNIX. То, какую форму вы
используете, зависит от того, что считается наиболее удобным и требует
как можно меньше нажатий клавиш.
Таблица 1-1
Три способа использования команды cd

Абсолютный CDPATH Относительно
переменной

cd /usr/russ cd cd $HOME
cd /usr/russ/src/asm cd asm cd $HSR/asm
cd /usr/russ/doc/paper/conf cd paper/conf cd $HD/paper/conf
cd /usr/russ/tmp cd tmp cd $HT

Строка 31 инициализирует переменную PATH. PATH работает таким же
образом, как CDPATH. Она ищет программы, которые нужно запустить, в
каждом каталоге, указанном в переменной PATH. Если имя не найдено ни в
одном из этих каталогов, печатается сообщение ": not found"
(""имя-файла": не найдено").
Поскольку мы можем установить наш PATH как угодно, можно указать
все таинственные места в системе, в которых расположены исполняемые
модули. Когда мы хотим их выполнить, мы не обязаны их искать и набирать
полное маршрутное имя. Чтобы дополнить PATH, введите, например,
следующее:
PATH=$PATH:/usr/lib/uucp
Команда paths, представленная далее в этой книге, использует
$PATH, чтобы сообщить нам, в каком каталоге размещен исполняемый модуль.
Строка 32 инициализирует переменную SHELL. Эту переменную могут
использовать не более чем одна или две утилиты. Обычно она устанавливается
системой, когда вы регистрируетесь. Строка 33 экспортирует переменные
CDPATH, PATH и SHELL.
Строки 35-39 - это хитрый способ установки определений терминалов.
Строка 35 начинается со спрятанной команды tty, заключенной в
знаки ударения (`...`). Выходом команды tty является "/dev/tty00". Затем
мы берем основное имя этой строки, т.е. "tty00". Далее мы используем
структуру переключателя по этому значению, чтобы увидеть, что мы
хотим сделать для каждого конкретного терминала. Команды tset, показанные
здесь, относятся к среде XENIX и могут быть неприемлемыми в вашей
среде.
Строка 41 делает эхо-отображение значения TERM на экран, чтобы
сообщить вам тип вашего терминала, если он вам нужен. Это значение
доступно, если описанная ранее команда tset устанавливает для вас TERM
как часть своей обычной работы.
В строке 42 устанавливается переменная TERMCAP, указывающая на
/etc/termcap. Это обычный способ установки переменной TERMCAP. Другой
способ - присвоить TERMCAP текущую закодированную строку, которая находится
в файле описания терминала. Если TERMCAP установлен на закодированную
строку, то утилите vi нет необходимости обращаться к файловому
вводу-выводу, чтобы получить характеристики вашего терминала. Строка
43 экспортирует эти значения так, чтобы они были доступны на любом
уровне интерпретатора shell.
Строка 45 устанавливает частотную переменную. Это переменная из
XENIX и, возможно, имеется в System V. Она используется для установки
информации о времени.
Строка 46 устанавливает информацию о зоне времени, как это требуется
в библиотечном вызове ctime(3). Имея переменную TZ, вы можете перекрыть
подразумеваемую зону времени при доступе ко времени из программы
на языке Си. Строка 47 экспортирует эти переменные.
Строка 49 устанавливает ваше значение маски пользователя (umask).
Она управляет подразумеваемым разрешением доступа для всех файлов, которые
вы создаете. Система вычитает значение umask из 777. Результат
становится правом доступа к файлу, в данном случае 755. Когда вы создаете
каталог с правом доступа 755, этот каталог показывается командой
"ls -l" как rwxr-xr-x. Когда вы создаете некаталоговый файл с правом
доступа 755, этот файл показывается как rw-r--r--, что эквивалентно
644. Некаталоговые файлы не имеют бита x, поэтому их нельзя исполнить.
Каталогам же нужен установленный бит x, чтобы они были доступны по команде
cd.
Строки 51 и 52 сообщают вам о времени вашего сеанса работы в
системе. Строка 51 сообщает вам текущее время вашего входа в систему,
а строка 52 вызывает программу lastlog, которая печатает дату вашей
последней регистрации в системе. Программа lastlog описана в главе 5.
Строки 54-58 инициализируют переменные, генерирующие цвета на
цветном мониторе. Управляющие значения являются стандартными значениями
кодов ANSI. Это работает в системе XENIX и может работать в вашей
системе. Растровая графика не доступна, но имеется символьная графика
и различные основные (foreground) и фоновые (background) цвета. Основные
цвета кодируются числами, начиная с 30, а фоновые цвета - числами
с 40.
Строки 60-66 - просто для забавы. Они представляют собой хитрый
способ устанавливать каждый день на экране различные цвета. Строка 60
начинается с запуска команды date и передачи ее выхода по конвейеру
команде cut. Вырезается первое поле, которое является днем недели. Затем
мы создаем структуру переключателя по строке дня, выполняя различные
действия для каждого дня. Благодаря эхо-отображению управляющих
последовательностей, монитор реагирует немедленно.
ТЕОРИЯ ОТНОСИТЕЛЬНОСТИ ВНУТРИ СИСТЕМЫ UNIX
Теперь, когда мы ознакомились с "домашней" средой, следующий шаг
- обратиться к средам, находящимся вне регистрационного каталога
($HOME). Например, что представляют собой другие каталоги на том же
уровне, что ваш $HOME ? Кто еще работает в системе? Как попроще получить
доступ к их каталогам? Можете ли вы запускать программы в чужих
каталогах? Такого рода вопросы и действия относятся к другим людям в
вашей системе.
Единственный способ ответить на эти вопросы - посмотреть вокруг
себя. Никто не собирается рассказывать вам, что такое система. Вы
должны сами исследовать ее и выяснить, куда вы можете ходить, а куда
нет. Система конечна, так что вы можете себе помочь, делая распечатки
всех каталогов и файлов.
Вы можете маневрировать в системе UNIX, используя относительную
нотацию. Поскольку системное дерево образовано из каталогов, обозначения
. и .. позволяют нам двигаться вверх и вниз по дереву. В любой
точке .. означает родительский каталог текущего каталога, в котором мы
находимся.
Ниже показаны некоторые примеры относительных команд.
ls -l $HOME/.. перечисляет файлы в моем родительском
каталоге.
cd ../../.. в предположении, что текущим каталогом
является /usr/russ/src/c, делает моим
текущим каталогом /usr.
ls . перечисляет файлы в текущем каталоге.
ls .. перечисляет файлы в моем родительском
каталоге.
$HOME/../../bin/ls запускает ls в каталоге
/usr/russ/../../bin, т.е. в /bin/ls.
../fred/bin/ls запускает команду ls в каталоге
двоичных модулей Фреда, который имеет
тот же родительский каталог, что и я,
т.е. /usr/fred/bin/ls.
ОБЩАЯ СИСТЕМНАЯ СРЕДА
Системная среда не просто НАХОДИТСЯ в системе UNIX, а ЯВЛЯЕТСЯ
системой UNIX. Как мы увидим в этой книге, вся система - UNIX, Си, команды,
файлы и т.д. - это просто логический подход к функционированию
компьютера. Программное обеспечение - это то, что определяет система
для конечного пользователя. Мы можем представлять все машины, работающие
в системе UNIX, как одинаковые и трактовать каждый UNIX как один и
тот же. Мы предполагаем, что реакция машины будет каждый раз одинаковой.
Мы можем смотреть на UNIX таким же образом, как на физические законы.
Мы ограничены ими, но мы также вольны применять эти законы в ситуациях
и областях, с которыми мы до этого никогда не встречались. Мы
можем доверять этим законам и допускать, что они применимы везде, куда
бы мы ни направились. Такова система UNIX, по крайней мере в идеале.
Система имеет много сред. Важно понимать, что они собой представляют,
как взаимодействуют и для чего могут быть использованы. Так же,
как
программы = структуры данных + алгоритмы
так и
UNIX = файловое дерево + утилиты
Среда UNIX - это сочетание двух важнейших вещей: файлового дерева
и интерфейса системных вызовов. Это дерево допускает бесконечное
расширение возможностей, позволяя монтировать внешние дисковые области
в любой точке файловой системы. Дерево помогает также в сборе логически
связанных файлов, что делает систему более организованной.
Интерфейс системных вызовов обеспечивает набор инструментов, из
которых можно построить большинство других функций. Определение интерфейса
System V имеется в виде типографской книги и может быть найдено
в книжных магазинах. Строгое следование этому стандарту гарантирует
совместимость с постоянно развивающейся AT&T System V.
ОБЩЕЕ ФАЙЛОВОЕ ДЕРЕВО
Для того чтобы лучше понять мир UNIX, посмотрите пример распечатки
структуры UNIX на рис.1-3 . Это наглядное представление полного дерева
корневой файловой системы. Любые другие расширения файловой
системы монтируются на эту файловую систему.
Точкой временного монтирования является /mnt. Более постоянные точки
монтирования должны быть созданы администратором, например /0, /1 и
т.д. или /usr1, /usr2 и т.д.
ПЕРВЫЙ СЛОЙ
Самым левым каталогом является /bin, который содержит все главные
двоичные утилиты. Это наибольший из двух основных каталогов двоичных
модулей.
Следующий каталог - /dev, в котором размещены все файлы устройств.
Файлы устройств являются точками доступа к периферии, подсоединенной
к системе. Этот файл привязан к периферии с помощью ядра и
драйвера устройства.
Административные утилиты и конфигурационные файлы хранятся в
/etc. Примерами являются getty и gettydef, init и inittab, а также
файл паролей (/etc/passwd).
Следующий каталог - /lib, где размещены библиотеки компилятора.
Здесь могут храниться и другие типы библиотек.
Каталог /lost+found используется утилитой fsck (главное средство
поддержания файловой системы) для хранения логически удаленных файлов.
Если на самом деле вы хотите сохранить эти файлы, они могут быть извлечены
из этой удерживающей области после завершения уборки файловой
системы.
Следующий каталог - /mnt. Это временная точка монтирования для
файловых систем. Мы часто монтируем и демонтируем файловые системы
просто для того, чтобы запустить быструю проверку чего-либо. Здесь подходящее
место для этого.
Главным временным рабочим каталогом системы является /tmp. Многие
утилиты, такие как vi, fsck, интерпретаторы shell и программы резервного
копирования, используют /tmp для хранения рабочих файлов.
Следующий каталог - /usr, который применяется как точка монтирования.
Файловая система, смонтированная здесь, содержит дополнительную
системную информацию и каталоги пользователей. Это разделение между
загружаемой файловой системой и пользовательской файловой системой было
сделано, чтобы сбалансировать загрузку диска. Если бы все важные
файлы были в одном разделе, он был бы слишком большим. Производительность
может быть ухудшена, если все действия направлены в одну логическую
область диска. Благодаря разбивке всей системы на две, каждая
файловая система поддерживает разумное количество свободного пространства.
Чуть ниже мы рассмотрим каталог /usr более подробно.
Последний файл - это само ядро, /unix. Весь /unix фактически существует
и представляет собой большой a.out (скомпилированный объектный
файл). Ядро изготавливается путем запуска ld на группе библиотек,
которые загружаются по очереди в огромный исполняемый модуль, называемый
/unix. Машина запускается с первых 512 байтов корневой файловой
системы. Программа начальной загрузки, которая находится здесь, загружает
программу загрузки большего размера, иногда называемую /boot.
/boot загружает и запускает /unix.
ВТОРОЙ СЛОЙ
Второй слой каталогов размещается под /usr. Как упоминалось ранее,
/usr используется как точка монтирования для другой файловой
системы. Это значит, что все файлы, которые имеются в /usr, находятся
в другом разделе загружаемого диска или вообще на другом диске.
Первым каталогом является adm, для администрирования. Он
содержит учетные файлы и регистрационный файл для su (суперпользователя),
а также другие административные файлы.
В каталоге bin имеются исполняемые модули, которые используются
менее часто, чем модули в двоичном каталоге корневого уровня (/bin).
Почти все исполняемые модули распределены между этими двумя каталогами.
Другие исполняемые модули рассеяны по всей системе, например
/usr/lib/uucp/uucico и /usr/lib/ ex3.7preserve.
Далее games. UNIX приходит с ассортиментом интересных игр. Большинство
из них текстовые, но предоставляется несколько программ графического
типа, например worm, worms и rain.
Каталог include содержит все файлы-заголовки. Файлы-заголовки
используются в программах на языке Си для определения структур и
системных присваиваний, полезных для программирования. Здесь имеется
подкаталог sys, который содержит все файлы= заголовки, относящиеся к
системе. Читая эти файлы-заголовки, можно многое узнать о системе
UNIX.
Следующий каталог - lib, который содержит библиотечные файлы для
всех видов "имущества": файлы печатающих устройств, файлы поддержки
утилиты vi, другие языки и uucp. Каталог /usr/ lib представляется
складом всяких библиотек, которые имеются в системе, отличных от библиотек
компилятора.
Каталог lost+found находится здесь для той же цели, что и одноименный
каталог корневого уровня. Каждая файловая система должна иметь
такой файл. Без него fsck не имеет временного места для размещения полуудаленных
файлов и поэтому удаляет их навсегда.
В каталоге mail находится ваш системный почтовый ящик. Когда вы
запускаете команду mail, здесь накапливается очередь сообщений. В каталоге
usr/mail каждый файл носит имя пользователя. В этом файле хранится
почта пользователя, пока он не прочитает ее.
Каталог man предназначен для активных страниц руководств по
системе UNIX. Наличие постоянного доступа к страницам руководств является
хорошим средством. Однако, эти страницы занимают много места, и
доступ к ним может потребовать довольно много времени при сильно загруженной
системе.
В каталоге news хранятся все файлы новостей. Эти файлы именованы
в соответствии с порядком, в котором они были введены в каталог. Команда
news(1) смотрит на дату файла $HOME/ .news_time, чтобы сообщить,
какие новости вы еще не читали.
Каталог preserve предназначен для файлов, связанных с утилитой
vi. Они помещаются сюда, когда вы работаете с vi или с редактором ex и
пропадает питание машины либо ваш сеанс работы прерывается в виде "зависания".
Когда в системе восстанавливается питание, /tmp содержит
файлы редактора ex. Из каталога /etc/rc запускается утилита
/usr/lib/ex3.7preserve, которая просматривает /tmp, преобразует его в
сохраненный файл и помещает его в /usr/preserve. Когда вы входите в
систему, вы получаете почту о том, что у вас имеется сохраненный файл
редактора, который вы можете восстановить и поместить его на исходное
место.
Каталог pub не содержит ничего особенного, обычно в нем просто
некоторые информационные файлы вроде таблицы ASCII или греческих символов.
Каталог spool - это главная точка входа для всех буферизованных
файлов в системе. В этом каталоге имеется много подкаталогов, содержащих
специфические типы буферизованных файлов. Некоторыми типичными
подкаталогами являются lp, uucp и uucppublic.
В каталоге src хранится исходный код системы UNIX, если он имеется
в системе. От этого каталога ответвляется много уровней: команды,
библиотеки, код ядра, код машинного языка и автономные утилиты. Часто
в /usr/src хранится также исходный код для локальной машины.
Каталог sys традиционно хранит файлы, необходимые для генерации
нового ядра. Это файлы-заголовки, конфигурационный файл, библиотеки и
командный файл для создания нового ядра из всех этих файлов.
Последний каталог - tmp. Это вторичная временная область хранения,
которая используется не так часто, как /tmp. Ее, однако, использует
утилита sort.
ЖИЗНЬ СИСТЕМЫ UNIX: НЕКОТОРЫЕ МЕТАФОРЫ
UNIX - это особый мир, живущий своей жизнью. Его социальная
структура имитирует реальную жизнь, с правительством, содержащим правителя
(корень root), штатом поддержки (bin, cron, lp, sys) и массами
(/usr/*). Массы не имеют доступа к мощи правителя, если не используют
предварительно установленных средств (/bin/su) или не занимаются криминальными
действиями и нарушением мер безопасности. Как и в любом обществе,
большая многопользовательская система UNIX устанавливает права
и обязанности своих пользователей.
При входе в систему пользователь получает свое "место под солнцем"
(регистрационный каталог - $HOME ). Это место зависит от того,
что было раньше (от родительского каталога ..), а будущие места зависят
от того, что происходит позже (каталоги, подчиненные $HOME).
Работа распределяется по организациям и иерархиям в зависимости
от их функций в обществе (все пользователи в /usr, все транзитные файлы
в /usr/spool, все функции безопасности в /etc). Посмотрите вокруг
себя в вашей системе, чтобы ознакомиться с вашим миром. Вы можете
после этого выбрать, участвовать ли в некоторой части этого мира или
игнорировать ее.
Движение людей в системе UNIX происходит параллельно. Некоторые
области (/tmp) доступны всем, а некоторые области сильно охраняются от
большинства людей (/etc/passwd). Транспортная служба может перевезти
наши вещи (передача файлов по сетям uucp). Мы даже можем воспользоваться
общественным транспортом, чтобы добраться в разные части города
(вход в другие системы (rlogin), эта особенность имеется только в
BSD).
В мире UNIX нам доступны различные пути. Эти пути помогают нам
сформировать свою судьбу (дисковые разделы, монтированные в любое
место файлового дерева). Когда дисковый пакет монтируется, он становится
доступным нам. Когда он демонтируется, мы теряем доступ к нему.
Когда запускаются процессы, они проходят через различные этапы
своей жизни. Они рождаются (ответвляются), растут (становятся планируемыми
и помещаются в таблицу процессов) и, наконец, становятся производительными
рабочими в обществе (переходят в состояние запуска и выполняются).
Все процессы имеют фамильное дерево. Порожденный процесс всегда
имеет родителя, а родительские процессы могут порождать много "детей".
В зависимости от приложения, они могут быть "дедами" и "внуками". Процессы
"умирают" так же легко, как создаются. Одной из необычных вещей
в мире UNIX является то, что "дети" почти всегда "умирают" раньше своих
"родителей".
Правительство (ядро) проводит в жизнь параметры среды, которые
выглядели бы в довольно тоталитарном духе, если бы это было в реальном
мире. Только определенное число рабочих допускается к рабочему месту
одновременно (это максимальное количество ячеек в таблице процессов).
Рабочие ограничены в числе "детей", которых они могут иметь (максимальное
количество процессов на пользователя). Поскольку рабочие накапливают
материальные ценности, они ограничены в количестве товаров,
которые они могут поместить в комнаты своих домов (максимальный размер
файла, или ulimit). Хотя не установлен лимит на число различных файлов
(комнат) максимального размера, которые могут существовать, вся система
имеет предел (df показывает свободное пространство), и одна ненасытная
персона может нанести удар по окружающим. Здесь возникает
своего рода экология.
Так же, как компьютерный век проходит под присмотром электронной
автоматики, так и UNIX ведет таблицы о деятельности всех пользователей.
Механизмы учета организованы правительством (внутри ядра) и всегда
записывают действия каждого пользователя. Тем не менее, это свободное
общество в той мере, что вы можете получить распечатку о вашем
кредитном состоянии (используя acctcom для печати учетных записей).
Хотя система UNIX имеет негативные аспекты (как и человеческое
общество), в ней есть также некоторые очень позитивные особенности.
Гибкость системы и богатство инструментов дает нам очень продуктивную
и детально разработанную рабочую среду. Наша производительность в этом
смысле ограничена в основном нашим собственным воображением. Когда работа
становится слишком утомительной и скучной, мы всегда можем создать
средства, делающие за нас эту работу. Это обстановка свободной инициативы,
в которой хорошие идеи могут дать значительное увеличение производительности.
* ГЛАВА 2. Доступ к файлам * Введение
2.1. Поиск файлов
2.1.1. tree - визуализация файлового дерева
2.1.2. thead - печать начала каждого файла
2.1.3. tgrep - поиск строк в дереве файловой системы
2.1.4. paths - нахождение пути доступа к исполняемым файлам, со
специальными опциями
2.2. Вывод информации
2.2.1. lc - вывод файловой информации на экран по столбцам
2.2.2. ll - вывод файловой информации в длинном формате
2.2.3. kind - вывод однотипных файлов
2.2.4. m - простой доступ к команде more
2.2.5. mmm - обработка программой nroff макрокоманд для рукописей
2.2.6. pall - печать всех файлов в дереве
ВВЕДЕНИЕ
В главе 1 был представлен обзор общей структуры системы UNIX и
показано, как взаимодействуют ее различные части. Это похоже на введение
в географию, когда на глобусе показывают континенты и крупные водные
пространства. Такая информация, хотя и является хорошим фундаментом
для общих знаний, вряд ли поможет найти наилучший путь из
Сан-Франциско в Лос-Анжелес. Необходим следующий уровень детализации:
названия поселений, дорог, развилок, улиц, адресов.
Файловая система UNIX похожа на континент со множеством городов
и, действительно, с адресами внутри городов. Каталоги и различные
уровни подкаталогов можно сравнить с маршрутами между различными пунктами
назначения, названия файлов - с адресами. Большое число путей и
мест назначения может выглядеть пугающе, но благодаря регулярности и
логичности, файловая система UNIX позволяет вам легко перемещаться
из одного места в другое, если вы знаете несколько основополагающих
принципов.
Будучи пользователями UNIX, все мы научились пользоваться основными
командами файловой информации, как, например, ls с различными опциями.
Мы знаем, как перемещаться между каталогами и копировать или
перемещать файлы. Тем не менее, находить нужную информацию о файлах из
всей массы информации не так-то легко. Нам необходимо создать инструментальные
средства, которые используют древовидную структуру файлов в
UNIX, чтобы находить то, что мы ищем, и, соответственно, выводить информацию
о файлах на экран, печатать листинги содержимого файлов и
т.д.
Эта глава знакомит с инструментальными средствами, которые облегчают
задачу поиска и доступа к файлам. Доступ к файлам может быть
обеспечен различными способами, поэтому техника и стиль меняются от
одного командного файла к другому. Например, в некоторых случаях вам
нужно найти имена всех файлов в данном сегменте файлового дерева, в
других случаях вас будут интересовать файлы только заданного типа:
текстовые файлы вообще или исходные файлы на языке Си в частности.
КОМБИНИРОВАНИЕ ПРОДУКТИВНЫХ ИДЕЙ
Две концепции являются общими почти для всех файловых инструментальных
средств. Первая - это идея рекурсивного поиска, которая означает,
что некоторые команды системы UNIX (например, find) просматривают
все файловое дерево, начиная с некоторой заданной начальной точки
(или с текущего каталога). Если в данном каталоге встречается подкаталог,
то его содержимое тоже исследуется - и так далее вниз к самому
нижнему под-подкаталогу. Так проходятся маршруты ко всем файлам в целом
дереве.
Стандартные команды системы UNIX обеспечивают только ограниченное
число основных функций, которые могут работать рекурсивно по всему
файловому дереву. Наша стратегия при создании инструментальных средств
в этой главе - воспользоваться преимуществами такого рекурсивного поиска
и распространить их на многие другие функции.
Вторая ключевая идея, связанная с полезными файловыми инструментальными
средствами - это возможность соединения команд с программными
каналами и управление потоком данных с помощью переадресации. Вероятно,
вы уже встречались с подобными особенностями в вашей собственной
работе с UNIX и эффективно их использовали. Возможно, вы еще не осознали,
что соединение рекурсивного поиска, предоставляемого некоторыми
стандартными командами, со специфическими функциями, предоставляемыми
другими командами, позволяет нам создать команды, которые автоматически
обходят обширные файловые деревья и извлекают нужную информацию.
(В следующей главе мы выйдем за пределы распечатки и отображения информации
на экран и научимся работать с файлами так, что мы сможем копировать,
перемещать и восстанавливать их по мере надобности.)
Для удобства мы сгруппируем инструментальные средства в два раздела:
поиск файлов и распечатка файловой информации. Имеет смысл
представлять их в таком порядке, так как вы сначала должны найти файл,
чтобы потом с ним работать.
ПОИСК ФАЙЛОВ
Этот раздел посвящен поиску файлов, где бы они ни находились, выводу
на экран выбранной информации и поиску символьных строк внутри
файлов.
Первая программа, tree, обходит все файловое дерево и печатает
имена всех файлов в формате визуального дерева. Она рекурсивно спускается
в каждый каталог и находит все его файлы, обеспечивая тем самым
глобальный осмотр файловых областей и их вложенной по глубине структуры.
Другое инструментальное средство - это thead. Thead печатает
несколько первых строк текстовых файлов, которые находятся в данном
сегменте файлового дерева. Просматривая заголовок, т.е. первые
несколько строк файла, вы можете получить достаточно информации, чтобы
идентифицировать содержимое файла. При вызове thead вы можете явно задать
каталог либо передать команде thead по конвейеру список полных
имен файлов. Это делает команду thead фильтром - особым видом команд
системы UNIX, который мы обсудим позже.
Следующее инструментальное средство - tgrep. Как следует из названия,
это еще одна команда, связанная с файловым деревом, которая
использует утилиту grep. Tgrep ищет символьные строки в каждом файле,
который находится в данном сегменте файлового дерева. Tgrep также является
фильтром, так что имена файлов можно передавать ей по конвейеру.
В нашем последнем проекте в этом разделе мы обратимся к использованию
каталогов как средства "навигации". Сначала мы опишем основной
алгоритм для утилиты, которая для каждого файла из заданного списка
файлов проверяет, находится ли этот файл в каком-либо каталоге по указанному
маршруту поиска. Затем мы построим paths - утилиту, которая
дополняет функцию поиска полезными опциями.
РАСПЕЧАТКА ФАЙЛОВОЙ ИНФОРМАЦИИ
Этот раздел знакомит вас с инструментальными средствами, предназначенными
для вывода на экран имен файлов и их содержимого. Инструменты
такого рода весьма полезны, так как они могут значительно уменьшить
количество необходимых символов, набираемых с клавиатуры при запуске
команды, и внести больше смысла в одну команду.
Первые два командных файла являются пре- и постпроцессорами для
команды ls. Команда lc выводит файловую информацию по столбцам, команда
ll перечисляет файлы в длинном формате. Эти командные файлы дополнены
опциями команды ls, чтобы сделать распечатки более информативными.
Так как команда ls используется довольно часто, упаковка наиболее
часто применяемых нажатий клавиш в командные файлы представляется целесообразной.
Упаковка уменьшает количество постоянно набираемых символов
и упрощает использование команд, исключает необходимость запоминания
подробного синтаксиса.
Третье инструментальное средство - это kind. Kind - еще один командный
файл препроцессорного типа, использующий команду UNIX file.
Команда file читает указанный файл и затем сообщает, является ли этот
файл текстовым, архивным или исполняемым. Поскольку распечатки команды
file не выбирают файлы заданного типа, возникает необходимость в создании
для этого специальной утилиты. Команда kind работает с распечаткой
команды file. Kind выводит на экран имена файлов только заданного
типа.
Еще один командный файл - m, который облегчает работу со стандартной
командой more системы UNIX, уменьшая количество необходимых
для запуска команды символов и упрощая интерфейс. Делается это без потери
гибкости: так же, как вы можете использовать команду more для
файла или передать команде more данные по программному каналу, вы можете
сделать то же самое для m.
Следующий командный файл - это mmm. Он состоит из одной заготовленной
командной строки для программы nroff системы UNIX. Существует
много способов вызова команды nroff и множество различных опций к ней.
Если же вы редко используете nroff, у вас могут возникнуть трудности в
запоминании специфических опций, необходимых для вашей работы с командой.
Эти проблемы отпадут, если у вас есть команда mmm. Определите опции,
которые вы обычно используете, и введите их в командный файл mmm
(о том, как это сделать практически, речь пойдет ниже). Теперь достаточно
набрать mmm - и вы имеете возможность работать с вашей командой
nroff.
Последняя утилита - pall. Pall обходит файловое дерево, ведя поиск
файлов заданного типа, и готовит их к выводу на принтер. Команда
pr системы UNIX используется для разбивки на страницы всех файлов
вместе и включения заголовков. Эта команда предлагает на рассмотрение
принтеру один большой файл и наиболее полезна в тех случаях, когда у
вас имеется множество каталогов с текстовыми файлами или с исходными
файлами программ.
Определив в общем основные наши задачи, перейдем к более близкому
знакомству с упомянутыми инструментальными средствами.
2.1. ПОИСК ФАЙЛОВ
2.1.1. tree - визуализация файлового дерева

ИМЯ: TREE

tree - вывод на экран структуры файлового дерева
НАЗНАЧЕНИЕ
Находит все файлы в файловом дереве и выводит на экран имена файлов,
показывая иерархическую структуру файлового дерева.
ФОРМАТ ВЫЗОВА
tree [dir]
ПРИМЕР ВЫЗОВА
$ tree $HOME
Выводит структуру файлового дерева регистрационного каталога.
ТЕКСТ ПРОГРАММЫ
1 :
2 # @(#) tree v1.0 Visual display of a file tree Author: Russ Sage
2а вывод на экран структуры файлового дерева
4 if [ "$#" -gt 1 ]
5 then echo "tree: wrong arg count""&2
6 echo "usage: tree [dir]" "&2
7 exit 2
8 fi
9 if [ "$#" -eq 1 ]
10 then if [ ! -d $1 ]
11 then echo "$0: $1 not a directory""&2
12 echo "usage: tree [dir]" "&2
13 exit 2
14 fi
15 fi
17 find ${1:-.} -print | sort | sed -e "1p" -e "1d" \
18 -e "s|[^/]*/| /|g" \
19 -e "s|[^ */|/|" \
20 -e "s|/\([^/]*\)$|\1|"
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ tree?
Как мы уже отмечали, вся система UNIX строится вокруг файловой
системы, которая похожа на дерево. Дерево, с которым мы работаем в
системе UNIX, растет вверх ногами: корень находится вверху, а ветви и
листва растут вниз от корня. Физическая структура реальных деревьев и
файловых деревьев, используемых в системе UNIX, очень сходна: один корень
(начальная точка) и один ствол. Как глубоко и как далеко могут
уходить ветви от основного ствола - не ограничивается ничем, кроме ограничений
физического пространства. Аналогично, число листьев, которые
может иметь каждая ветвь, фактически не ограничено.
Многое в системе UNIX задумано для того, чтобы приспособиться к
дереву. Некоторые команды обходят дерево и сообщают о его компонентах,
но обычно их сообщения выдаются в форме, не очень удобной для чтения
человеком. Это делает командные файлы весьма мощными инструментами.
Перевести необработанные, недружественные сообщения командных файлов в
удобный, информативный вид довольно легко.
Команда tree является комбинацией команд системы UNIX, которые
представляют логическую файловую структуру в наглядной форме. Эта команда
полезна для получения глобальной картины файлов, их расположения
в иерархической структуре файлового дерева, гнездовой структуры каталогов
и подкаталогов.
ЧТО ДЕЛАЕТ tree?
Команда tree - это постпроцессор для команды UNIX find. Find
просматривает сегмент файлового дерева и полные имена всех файлов, которые
соответствуют заданному критерию. Команда tree использует утилиту
sed системы UNIX, чтобы перевести выход команды find в наглядную
форму.
Входным параметром для команды tree является имя каталога, которое
может быть указано в любом абсолютном виде, например,
/usr/spool/uucp, или в относительном, например, ../../bin. Если никакого
имени не указано, подразумевается ., что является текущим каталогом.
Имя каталога является началом (или корнем) отображаемого дерева.
Чтобы показать глубину дерева, все файлы, подчиненные данному каталогу,
отображаются с отступом. Для удобства представления гнездовой
структуры, между следующими друг за другом ответвлениями печатается
косая черта (/).
Рассмотрим пример структуры каталога. Пусть корневым каталогом
будет /tmp с двумя каталогами: a и b. В каталоге a находится подкаталог
aa, который содержит файл file1, а в каталоге b , соответственно,
подкаталог bb, содержащий файл file2. Команда find выдаст распечатку
такого вида:
# find /tmp -print
/tmp
/tmp/a
/tmp/a/aa
/tmp/a/aa/file1
/tmp/b
/tmp/b/bb
/tmp/b/bb/file2
Как видно из этого листинга, файлы a и aa есть каталоги, а файл
file1 находится внизу файлового дерева. Сравните этот результат с результатом,
который выдает команда tree, используя утилиту sed.
# tree /tmp
/tmp
/ a
/ / aa
/ / / file1
/ b
/ / bb
/ / / file2
Корневым каталогом в этом листинге является каталог /tmp. Там,
где дерево переходит на более глубокий уровень, печатаются только символы
косой черты. Первый уровень - /tmp, под этим уровнем находятся
файлы-каталоги a и b, затем, соответственно, их подкаталоги aa и bb.
Исходя из этого листинга, мы делаем вывод, что на первом уровне каталога
находятся два файла (и эти файлы в действительности являются каталогами)
и что два файла находятся в подчиненных каталогах. Отметим,
что мы смогли идентифицировать aa и bb как каталоги только потому, что
в них присутствуют файлы file1 и file2.
Сравните этот листинг с выходом "необработанной" команды find.
Выход команды tree исключает отвлекающее внимание повторение элементов
путей доступа при каждом переходе к более низкому уровню. Благодаря
этому, сразу же видно СУЩЕСТВЕННУЮ информацию. Вот что мы имеем в виду,
когда говорим о создании более наглядного для человека интерфейса
с системой UNIX.
ПРИМЕРЫ
1. $ tree
Использует подразумеваемый каталог (текущий каталог, что равносильно
команде "$ tree .") в качестве начала файлового дерева.
2. $ tree /
Печатает древовидный листинг для КАЖДОГО файла всей системы. Команда
find при таком ее запуске начинает с корневого каталога и выдает
информацию о всех файлах системы.
3. $ tree $HOME/..
Показывает древовидный формат для всех других пользователей
системы (предполагается, что все пользовательские каталоги находятся в
одном и том же каталоге, например /usr/*).
ПОЯСНЕНИЯ
Первая строка содержит только знак двоеточия (:) - "нулевую команду".
Это связано с тем, что все командные файлы, описываемые в этой
книге, сделаны так, чтобы их можно было запускать в среде интерпретатора
Bourne shell. Наш комментарий в строке 2, идентифицирующий
версию, начинается со знака решетки (#). Си-shell ищет этот знак как
первый знак командного файла. Если он найден, то предпринимается попытка
выполнить данный командный файл. В противном случае Си-shell передает
командный файл интерпретатору Bourne shell. Вот почему мы не
хотим начинать первую строку со знака #. Мы, конечно, могли бы оставить
первую строку чистой, но чистая строка невидима и может быть случайно
удалена. Соответственно мы будем использовать чистые строки в
других случаях, чтобы выделить важные участки программы.
Строка 2 идентифицирует версию. Символьная строка @(#) есть специальная
последовательность в строке комментария, которая распознается
как строка "what" ("что"). Команда what в системе UNIX читает файл и
печатает сообщение, которое следует за строкой "what". Чтобы идентифицировать
версию данного командного файла, наберите
# what tree
и будет напечатано следующее сообщение:
tree:
tree v1.0 Visual display of a file tree Author: Russ Sage
Строки 4-7 проверяют, не слишком ли много аргументов было передано
командной строке. Это осуществляется путем исследования переменной
$#, которая представляет собой счетчик числа позиционных параметров
командной строки. Если насчитывается более одного параметра, печатается
соответствующее сообщение об ошибке в стандартный файл ошибок
(stderr) и программа останавливается с плохим значением статуса.
Отметим, что команда echo обычно печатает в стандартный выход
(stdout). Мы можем перенаправить stdout в другой файловый дескриптор,
указав его. В данном случае мы собираемся печатать в stderr. Синтаксис
переводится так: "вывести эту строку и перенаправить ее в файловый
дескриптор (&) стандартного файла ошибок (2)". Печать сообщений об
ошибках в stderr обеспечивает согласованное поведение командного файла
независимо от среды, в которой он запущен.
Отметим также, что коды статуса выхода в интерпретаторе shell
противоположны тем, которые используются при программировании на языке
Си. В Си истинное значение есть 1, ложное отлично от 1. При программировании
на языке shell успешным статусом выхода (истиной) является 0,
а плохим статусом (ложью) ненулевое значение.
Вы, возможно, удивитесь, почему мы так беспокоимся о том, чтобы
вернуть ложный статус выхода, если командный файл собирается просто
напечатать сообщение об ошибке и прекратить работу. Дело в том, что
все инструментальные средства системы UNIX должны быть спроектированы
так, чтобы они могли быть связаны с другими командами и процессами, в
которые они могут быть встроены. Возможно, что другой команде необходимо
будет вызвать команду tree и проверить, корректно ли она отработала.
Хорошим стилем проектирования программных средств является прогнозирование
и разрешение многих способов использования программы.
Строки 9-15 проверяют, чтобы любые параметры, передаваемые командной
строке, были действительно каталогами, как указано в нашем
синтаксисе. Напомним, что в командной строке может быть помещен только
один каталог. Если мы используем только один параметр и этот параметр
не является каталогом, то мы печатаем сообщение об ошибке и выходим.
Таким образом, операторы проверки гарантируют, что либо не используется
ни один параметр, либо единственный используемый параметр является
корректным каталогом.
Мы подошли к сердцу команды tree - это строки 17-20. Главным
здесь является команда find системы UNIX. Каталог, в котором ведется
поиск, определяется при запуске команды. Синтаксис ${1:-.} является
формой параметрической подстановки и означает следующее: если $1 (что
является первым позиционным параметром) установлен (иными словами,
если аргумент был передан командной строке и был ненулевым), то нужно
использовать это значение. В противном случае следует использовать каталог
. (текущий каталог). Этот тип подстановки дает нам возможность
запускать команду tree без указания имени каталога (когда после tree
на командной строке ничего не следует),- то есть работать в режиме "по
умолчанию", что часто используется в различных файловых инструментах.
Команда find выводит на печать полное имя каждого файла, который
ей встречается. Поскольку не используется никакая специальная опция
для селекции файлов, печатаются все имена. После этого все полные имена
файлов сортируются для более удобного чтения. Такая сортировка
несколько увеличивает время работы команды, однако наглядность результата
говорит о том, что это время было потрачено с пользой.
Далее, отсортированные полные имена файлов передаются по программному
каналу команде sed системы Unix. Sed - это "потоковый редактор",
очень гибкое средство, которое может быть использовано для идентификации
и обработки различных образцов текста. Опции -e являются
операциями редактирования, применяемыми к поступающим данным. Первый
оператор просто сообщает команде sed, что нужно напечатать первую
строку, затем удалить строку 1. Это делается для того, чтобы напечатать
название корневого каталога, который исследуется. Этой строке не
требуется никакой дальнейшей модификации, так как корневой каталог не
имеет никаких дополнительных элементов путей доступа, которые нужно
было бы трансформировать в символы косой черты для показа отступов.
Удаление первой строки связано с тем, что она не нужна в дальнейшей
работе.
Вторая операция редактирования является командой подстановки. Она
заменяет каждый символ, отличный от символа косой черты (вплоть до
первого символа /) на последовательность пробелов и затем один символ
(/). Это избавляет нас от печатания имен промежуточных каталогов впереди
полного имени файла. Буква g в конце этой строки означает, что
эта операция выполняется глобально, то есть для всех считываемых символов.
Итак, теперь строка состоит из начального элемента пути и одной
или более последовательностей пробелов, разделенных символами косой
черты. Символы обратной косой черты (\) в конце операций редактирования
- это символы продолжения, которые сообщают команде sed, что нужно
продолжить работу со следующей строкой в текущем пакете операций редактирования.
Третья операция редактирования (строка 19) также является командой
подстановки и заменяет каждый символ, который не является пробелом
(вплоть до символа /) на "не символ" и один символ косой черты. Этот
оператор удаляет пробелы из предыдущего результата редактирования и
смещает символ в самую левую позицию. Это создает гнездовую индикацию,
которую мы видели в предыдущем примере.
Последняя операция редактирования (в строке 20) заменяет символ
косой черты и все отличные от него символы (до конца строки) просто на
символы, отличные от /. Отметим, что это устраняет самый правый символ
/, который присутствует в листинге команды find. В результате остается
имя подчиненного файла, сдвинутое вправо.
Отметим синтаксис \1 команды sed - признак, относящийся к первому
(в данном случае единственному) регулярному выражению в скобках, которое
ему предшествует. В данном случае команде sed указано пройти символы,
соответствующие регулярному выражению - символы, отличные от /.
2.1.2. thead - печать начала каждого файла

ИМЯ: thead

thеаd Печатает заголовок (первые несколько строк) файлов.
НАЗНАЧЕНИЕ
Пройти файловое дерево и напечатать первые несколько строк каждого
файла. Если не указан каталог, то thead действует как фильтр.
ФОРМАТ ВЫЗОВА
thead [dir...]
ПРИМЕР ВЫЗОВА
$ find $HOME/src -name "*.c" -print | sort | thead
Печатает заголовки (первые несколько строк) всех моих исходных
файлов на языке Си.
ТЕКСТ ПРОГРАММЫ
1 :
2 # @(#) thead v1.0 Prints head of files in tree Author: Russ Sage
2а Печатает заголовки файлов в дереве
4 if [ "`echo $1|cut -c1`" = "-" ]
5 then echo "$0: arg error"
6 echo "usage: $0 [dir ...]"
7 exit 1
8 fi
10 case $# in
11 0) while read FILE
12 do
13 if file $FILE | fgrep text "/dev/null 2"&1
14 then echo "\n:::::::::::::::::::::"
15 echo " $FILE"
16 echo "\n:::::::::::::::::::::"
17 head -15 $FILE
18 fi
19 done;;
20 *) for NAME in $*
21 do
22 find $NAME -type f -print | sort | wile read FILE
23 do
24 if file $FILE | fgrep text "/dev/null 2"&1
25 then echo "\n:::::::::::::::::::::"
26 echo " $FILE"
27 echo "\n:::::::::::::::::::::"
28 head -15 $FILE
29 fi
30 done
31 done;;
32 esac
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ
FILE Содержит имя каждого файла
NAME Имя каталога, заданное в командной строке
ОПИСАНИЕ
ЗАЧЕМ НУЖЕН КОМАНДНЫЙ ФАЙЛ thead?
Как уже объяснялось ранее в этой главе, иерархическая файловая
система является очень значительной частью системы UNIX. Однако, только
несколько команд в UNIX имеют дело непосредственно с рекурсивным
поиском файлов. Единственный способ расширить возможности системы -
создать новые рекурсивные утилиты, работающие с файлами. В данном случае
мы соединим нашу стратегию поиска по дереву с командой head системы
UNIX для упрощения идентификации содержимого всех файлов в выделенном
сегменте файлового дерева.
Иногда у нас, возможно, будет возникать желание просмотреть файлы
в более чем одном каталоге. В больших проектах разработки программного
обеспечения файлы обычно создаются в нескольких иерархических каталогах.
Thead может работать со множеством путей доступа и выводить заголовки
(несколько первых строк файлов) в виде непрерывного потока.
ЧТО ДЕЛАЕТ thead?
Thead - это препроцессорная команда к команде head системы UNIX.
Команда head очень примитивна, но, добавляя к ней управляющую структуру
и логику, мы можем создать очень полезное инструментальное
средство, которого нет в стандартной среде UNIX.
Например, мы захотели просмотреть заголовки всех текстовых файлов
в нашем регистрационном каталоге. Если у нас имеется большое число
подкаталогов, нам необходимо все их пройти и просмотреть все файлы,
содержащиеся в них. Мы можем сделать это с помощью команды
$ thead $HOME
Если мы хотим просмотреть только исходные файлы на языке Си
(*.c), то представленный выше синтаксис не годится для этого. Он не
обладает достаточной гибкостью. Нам необходимо иметь способ указать
(или квалифицировать) файлы в $HOME перед тем, как просматривать их.
Так как команда thead может воспринимать полные имена файлов, мы можем
использовать следующую команду:
$ find $HOME -name "*.c" -print | sort | thead
Команда find генерирует список файлов с расширением C, который
сортируется и подается по каналу на вход команде thead.
Как видно из представленных двух примеров, весьма полезной для
командного файла является возможность получать входные данные либо из
аргументов командной строки (как в первом примере), либо по программному
каналу (как во втором примере). Способность использовать программный
канал позволяет вам применять какие-либо другие команды системы
UNIX, которые могут отбирать входные данные для вашего командного
файла. Команда с такой двойной возможностью называется ФИЛЬТРОМ. Среди
стандартных команд системы UNIX вы найдете лишь несколько команд-фильтров,
таких как wc, awk, sort.
Эти два способа поступления входных данных в программы делают интерфейс
с командными файлами очень гибким. Мы можем подстраивать програмные
средства под наши нужды, а не подстраивать наши желания под
имеющееся программное обеспечение.
Аргументами для команды thead являются каталоги. Никаких опций,
начинающихся со знака "-" нет, только каталог или полные имена файлов.
Команда thead знает из синтаксиса, какой способ запуска команды будет
использоваться. Если командная строка содержит имя файла, thead
просмотрит все позиционные параметры. Если никакие имена не указаны,
thead читает стандартный ввод (stdin) и останавливается, когда встречает
EOF. (Такое бывает в случае, когда команда thead получает входные
из программного канала.)
Для каждого файла, с которым работает thead, выполняется контроль
- текстовый ли это файл. Применение команды head к исполняемым модулям
приводит к выводу "таинственных" символов на экран и иногда может вызвать
дамп оперативной памяти.
ПРИМЕРЫ
1. $ thead /etc
Печатает данные из каждого текстового файла, находящегося в каталоге
/etc. Очень полезная команда, так как большинство файлов в /etc
являются исполняемыми модулями. Удобно иметь возможность быстро изолировать
текстовые файлы.
2. $ thead /usr/include
Просматривает все подключаемые файлы (*.h), даже в системном подкаталоге
sys.
3. $ find $HOME -ctime 0 -print | thead
Ищет все файлы в вашем регистрационном каталоге, которые были изменены
в течении последних 24 часов. Для каждого файла проверяется,
текстовый ли он. Если файл текстовый, то он печатается.
ПОЯСНЕНИЯ
Строки 4-8 выполняют проверку ошибок. Так как команда thead не
имеет никаких опций, любые позиционные параметры, которые начинаются с
дефиса (-) являются неверными. Если первым символом первого позиционного
параметра оказывается "-", то печатается сообщение "argument
error" (ошибка аргумента) вместе с сообщением о способе запуска и команда
thead прекращает работу.
Некоторые приемы программирования для интерпретатора shell,
используемые в этих строках, довольно часто встречаются в данной книге,
поэтому имеет смысл остановиться на них подробнее.
Проанализируем строку 4, работающую изнутри наружу. Команда echo
выдает содержимое $1 (текущий параметр командной строки), которое передается
по программному каналу команде cut. Команда cut используется
для того, чтобы выделить определенные символы или группы символов из
строки. В данном случае опция -c1 используется для получения только
первого символа.
КОМАНДА cut ДЛЯ BSD
В системе BSD нет команды cut, но следующий командный файл все же
"вырезает" первое непустое поле в текущем аргументе.
Предположим, мы используем команду для генерации целого набора
строк. В данном случае это команда who:
for NAME in 'who | sed "s/^\([^ ]*\).*/\1/"'
do
done
Для каждой обнаруженной строки (аргумента) команда sed должна
подставить вторую строку символов вместо первой строки. Первая строка
- это строка, которая вырезается. Мы ищем от начала строки (^) символ,
отличный от пробела ([^ ]), за которым следует любое число непустых
символов (*). Эта операция прерывается по достижении пробела. Набор
непустых символов ограничивается обратными косыми чертами \( и \).
Впоследствии ссылка на этот набор дается в виде \1. Символы .* означают,
что после того, как найден пробел, необходимо считать подходящими
все символы до конца строки. Мы находимся фактически сразу после того,
что заключено в пару символов \( и \). Группируя первый набор символов,
отличных от пробела, мы получаем то, что является результатом работы
команды "cut -f1".
В этом месте мы подходим к знакам ударения (`), окаймляющим все
выражение. Они берут результат работы всех команд, заключенных в знаки
ударения и передают на следующую охватывающую структуру в наших вложенных
выражениях. Этот следующий уровень окаймления указан кавычками.
Кавычки превращают символ в строку, чтобы его можно было сравнить с
символом "-". Следующий слой - квадратные скобки, указывающие условие
для оператора if. Это приводит к тому, что генерируется нулевое (истина)
или ненулевое (ложь) условие, которое управляет тем, будет ли выполнена
часть then оператора if-then.
Мы не собираемся подробно анализировать много строк данного командного
файла, но мы хотим показать вам, как читать выражение или всю
строку текста программы так, чтобы это имело смысл.
Остальная часть командного файла представляет собой один огромный
оператор выбора (case). Аргументом, используемым для ветвления, является
число позиционных параметров в командной строке. Если позиционных
параметров нет, то в строках 11-19 активируется цикл while. Заметим,
что цикл while выполняет оператор чтения, но не указывает, откуда должен
быть взят его вход. Это связано с тем, что входом по умолчанию является
стандартный ввод (stdin). Для каждого имени файла, которое читается
из стандартного ввода, запускается команда file системы UNIX.
Выход команды file передается по программному каналу команде fgrep (а
не grep, что увеличивает скорость), чтобы посмотреть, является ли файл
текстовым.
Фактический выход команды fgrep перенаправляется на нулевое устройство
(в бесконечную область памяти), поскольку он нам не нужен.
Нас интересует лишь код возврата после выполнения всего конвейера.
Если команды file и fgrep отработали успешно, кодом возврата является
ноль. Это истинное значение, поэтому выполняется участок цикла
после then (строки 14-17). Если файл не существует или не является
текстовым, то код возврата ненулевой, и условный оператор завершается.
Это приводит нас в конец цикла, выполняется следующая итерация цикла
while и мы рассматриваем следующий аргумент из стандартного ввода.
Теперь рассмотрим обработку, выполняемую по then (строки 14-17).
Для каждого файла, который является текстовым, печатается строка двоеточий
(:) до и после имени файла, а команда head системы UNIX печатает
первые 15 строк. Такой сценарий продолжается, пока не закончатся данные
в стандартном вводе.
Рассмотрим другую альтернативу, покрываемую данным оператором выбора.
Она обрабатывает ситуацию, когда имеется несколько позиционных
параметров (что указано символом * в операторе case). Цикл for пробегает
все параметры (строка 20). Звездочка (*) в операторе case означает,
что подходит любое значение, которое не подошло ранее. Это улавливающая
(catchall) опция. Цикл for использует аргумент $* в качестве
своего входа. Он представляет значения всех позиционных параметров,
что является фактически всей командной строкой, исключая имя утилиты.
Команда find используется для поиска всех нормальных файлов в каталоге.
"Нормальные" файлы не означает "только текстовые файлы", поэтому
мы проверим это позже. Выход команды find передается по каналу
команде sort, чтобы сделать его более наглядным. Отсортированный
список передается по каналу в цикл while, который помещает имя файла в
переменную FILE (строка 27). Проверяется, текстовый ли файл, затем он
печатается командой head.
Если мы сравним строки 13-18 и строки 24-29, то мы увидим, что
это один и тот же код. В большинстве языков программирования это означало
бы, что мы должны оформить эти строки как процедуру и вызывать
ее, когда нужно. Язык программирования интерпретатора shell, хотя и
довольно мощный, не имеет хорошего способа реализации процедур.
Последний интерпретатор shell в System V имеет функции, которые позволяют
решить эти проблемы.
Отметим, что внутренний цикл while повторяется на каждом файле,
который существует в определенном каталоге, а внешний цикл for проходит
от каталога к каталогу.
ВОЗМОЖНЫЕ МОДИФИКАЦИИ
Для увеличения гибкости хорошо бы добавить опции, чтобы вы могли
переходить на команду find непосредственно из thead. Полезными аргументами
были бы -name для изолирования образцов имен файлов и -ctime
для обработки изменений, связанных со временем.
Еще одной привлекательной особенностью было бы добавление опции
грамматического разбора (основанной на -) и опции -n, указывающей, что
из команды head должно быть напечатано n строк.
ВОЗМОЖНЫЕ ИССЛЕДОВАНИЯ
В чем отличие между двумя следующими операторами?
$ find $HOME -name "*.c" -print | thead
и
$ find $HOME -name "*.c" -exec head {} \;
Они выглядят очень похоже, и они действительно похожи. Они обрабатывают
одни и те же файлы и печатают одни и те же данные из каждого
файла. Основное отличие в том, что строка, которая использует thead,
печатает хорошее оформление вокруг имени файла, а чистая команда find
печатает непрерывный поток текста так, что очень трудно определить,
какой файл вы просматриваете.
2.1.3. tgrep - поиск строк в дереве файловой системы

ИМЯ: tgrep

tgrep Поиск строки по шаблону в дереве файлов
НАЗНАЧЕНИЕ
Обходит файловое дерево и ищет в каждом файле указанную строку.
Если не указан никакой каталог, tgrep действует как фильтр.
ФОРМАТ ВЫЗОВА
tgrep [-c|-h] string [file ...]
ПРИМЕР ВЫЗОВА
# tgrep "profanity" /
Поиск слова "profanity" по всей системе (суперпользователь снова
на тропе войны!)
ТЕКСТ ПРОГРАММЫ
1 :
2 # @(#) tgrep v1.0 Search for string in tree Author: Russ Sage
2а Поиск строки в дереве
4 OPT=""
6 for ARG in $@
7 do
8 if [ "`echo $ARG|cut -c1`" = "-" ]
9 then case $ARG in
10 -c) OPT="-name \"*.c\""
11 shift;;
12 -h) OPT="-name \"*.h\""
13 shift;;
14 *) echo "$O: incorrect argument" "&2
15 echo "usage: $O [-c|-h] string [file ...] "&2
16 exit 1;;
17 esac
19 done
21 case $# in
22 0) echo "$O: argument error" "&2
23 echo "usage: $O [-c|-h] string [dir ...]" "&2
24 exit 2
25 ;;
26 1) while read FILE
27 do
28 grep -y "$1" $FILE /dev/nul
29 done
30 ;;
31 *) STRING=$1; shift
32 eval find "$@" -type f $OPT -print | sort | while read FILE
33 do
34 grep -y "$STRING" $FILE /dev/null
35 done
36 ;;
37 esac
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ
FILE Содержит имя каждого файла
OPT Содержит специальные опции команды find
STRING Временная переменная, в которой содержится строка
поиска
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ tgrep?
Как мы могли видеть на примере двух предыдущих утилит, рекурсивный
просмотр файлов очень полезен. Он сохраняет время, поскольку позволяет
избежать поиска файлов вручную, а также создает средства, которые
могут быть использованы в более мощных утилитах. Чем больше имеется
созданных нами средств, тем больше новых средств мы можем построить
с их помощью. Единственная проблема заключается в том, что вы
должны позаботиться об их взаимозависимости (каким утилитам или
средствам требуются другие утилиты или средства и кто на кого влияет).
Еще одна область, где UNIX не имеет "родной" рекурсивной команды
- это обработка строк. Семейство команд типа grep очень велико, но все
они работают только по одному фиксированному маршрутному имени файла.
Нам необходим препроцессор для команды grep. Правда, мы можем дать
запрос на все файлы во всей системе или какой-либо ее части по нашему
выбору. Если мы попытаемся сделать это вручную, то это означает, что
мы должны много раз нажимать на клавиши, что может привести к синтаксической
ошибке. Вы также должны точно помнить, как вы создавали
командную строку, если вы в следующий раз захотите выполнить такую же
задачу. Зачем выполнять грязную работу? Для этого существует компьютер.
Создавая программу автоматического обхода дерева файлов, мы освобождаемся
для того, чтобы направить нашу энергию на более важные вещи
вместо того, чтобы выпутываться из ситуации в случае какого-либо слишком
специфичного синтаксиса. Один раз создав достаточно мощные
средства доступа к файлам, мы можем посвятить наше время написанию
программ, обрабатывающих файлы данных для решения каких-либо задач.
ЧТО ДЕЛАЕТ tgrep?
Основным предназначением tgrep является обеспечение большей гибкости
и легкости использования возможностей команды grep. Ее синтаксис
точно такой же, как и у grep, за исключением допустимых типов файлов.
В команде grep UNIX в качестве аргумента может указываться практически
любой файл, но указание текстового файла имеет наибольший смысл. В команде
tgrep также могут использоваться текстовые файлы, но наибольший
смысл имеет указание каталогов, поскольку мы ищем имена файлов. Команда
find работала бы не очень хорошо, если бы пыталась извлечь множество
имен файлов из текстового файла. В командной строке может указываться
множество имен каталогов, поскольку все они используются как
начальное место поиска оператора find.
По умолчанию tgrep находит все обычные файлы. В этой программе
нет никакой проверки на то, текстовый файл или нет, поскольку мы не
пытаемся напечатать все на экран. Поэтому мы ищем строки символов во
всех файлах, начиная от архивных и заканчивая исполняемыми.
Если вы хотите выбрать типы файлов, используйте две опции, -c и
-h. Опция -c заставляет команду UNIX find искать только файлы с именами
вида *.c. Аналогично, опция -h соответствует файлам *.h. Эти опции
могут быть полезны для управления программами на языке Си, с которыми
мы более детально познакомимся в главе 4. Эти опции не самое важное,
но они показывают, как легко добавить новые опции в программу.
Если при вызове была указана какая-то иная опция, кроме упомянутых,
выводится сообщение об ошибке и программа останавливается. Подобно
thead, tgrep является фильтром.
ПРИМЕРЫ
1. $ tgrep unix $HOME
Поиск любого вхождения слова unix во всех файлах моего регистрационного
каталога.
2. $ tgrep -c "^sleep()$" $HOME/src
Поиск выражения (начало строки, символьная строка, конец строки)
во всех исходных файлах на языке Си в регистрационном каталоге с
исходными текстами (опция -c).
3. # find /usr/src -name "*.c" -print | tgrep "ioctl"
Поиск всех вызовов ioctl в исходных Си-файлах, начиная с каталога
/usr/src. (Обратите внимание, что я являюсь суперпользователем. Это
видно из того, что я занимаюсь поиском в ограниченной части системы, а
именно в исходных дистрибутивах, а также из того, что в качестве символа
приглашения используется символ "#".)
4. $ tgrep "| more" `find . -type f -print`
Поиск символа вертикальной черты (|), после которого следует слово
more, в списке имен файлов, генерируемом оператором find. Find печатает
имена всех файлов текущего каталога и всех подкаталогов, которые
являются обычными файлами.
5. $ tgrep trap /bin /usr/bin /etc
Поиск команды прерывания (trap) во всех командных файлах интерпретатора
shell, которые имеются в трех каталогах.
ПОЯСНЕНИЯ
В строке 4 переменная OPT, в которой хранятся необязательные команды
оператора find, инициализируется в нулевое значение.
Строки 6-18 выполняют проверку на наличие ошибок. Проверяется,
является ли первым символом каждого позиционного параметра символ "-".
Если проверка успешна, то проверяется на корректность сам аргумент.
Возможными опциями являются -c и -h. Если указано что-либо другое, выводится
сообщение об ошибке и программа завершается. Обратите внимание,
что с помощью команды shift допустимые опции удаляются из командной
строки. Это сделано для того, чтобы впоследствии выражение $@ могло
быть использовано для получения только аргументов строки поиска и
маршрута. Выражение $@ является другой формой выражения $ *, в которой
оно распространяется на все позиционные параметры. Однако в последующем
тексте мы увидим одно большое отличие между ними.
Еще один трюк сделан при присвоении значения переменной OPT в
строке 10. Этой переменной нам необходимо присвоить значение, которое
включает в себя пару кавычек, поскольку кавычки должны быть частью
строки поиска, которая в конце концов используется оператором find.
Однако обнаружение второй кавычки обычно ЗАВЕРШАЕТ оператор присваивания,
а мы этого в данном случае не хотим. Выходом из ситуации является
использование символа \, который отменяет специальное значение следующего
за ним символа. В данном случае специальное значение было бы концом
строки присваивания.
Таким образом, кавычка перед звездочкой (*) в строке 10 рассматривается
как часть значения переменной OPT, вместо того, чтобы завершать
операцию присваивания. Следующая встреченная кавычка также должна
быть сохранена в значении переменной, чтобы указать конец хранимой
здесь строки поиска, поэтому она также экранирована символом \.
Последняя кавычка в этой строке не экранирована обратной косой чертой.
Эта кавычка соответствует своей обычной функции в смысле интерпретатора
shell и завершает оператор присваивания.
Вам нужно поэкспериментировать с использованием кавычек, чтобы
понять его. Когда какой-то командный файл не желает работать правильно,
но ошибок не видно, проверьте правильность использования кавычек.
Оставшаяся часть командного файла - это оператор case (строки
21-37), который имеет дело с числом аргументов командной строки. Если
нет никаких аргументов, печатается сообщение об ошибке и мы выходим из
программы. Мы ведь не можем делать никакого поиска командой grep, если
мы даже не знаем, какую строку нужно искать.
Если указан только один аргумент, то это должна быть строка поиска.
Это также означает, что в командной строке не указаны имена файлов
и поэтому мы читаем их со стандартного устройства ввода, т.е. командный
файл действует как фильтр. Цикл while (строки 26-29) читает со
стандартного ввода имя каждого файла для поиска в нем командой grep
нужной строки. Опция -y команды grep означает нечувствительность к регистру
символов при поиске. Использование этой опции дает нам хорошие
шансы попасть на нужную строку.
Другой интересной особенностью этого цикла являются две команды
grep в строках 28 и 34. Мы ищем нужную строку не только в файле, но
также в каталоге /dev/null. Это кажется странным? Да. Проблема заключается
в самой команде grep. Grep знает, когда в командной строке указано
более одного файла. Если имеется более одного файла, то имя файла
выводится на экран до вывода найденной строки. Если же в командной
строке указан только один файл, то его имя не выводится, поскольку
считается, что пользователь должен помнить это имя. Это создает проблемы
при написании командных файлов, когда вы имеете много имен файлов,
но они обрабатываются по одному. Мы обрабатываем файлы по одному,
поскольку если бы мы использовали указание группового имени файлов с
помощью метасимволов, то могло бы оказаться слишком много имен файлов
в одной командной строке для команды grep. Поэтому вместо использования
некоторой замысловатой схемы для сокращения количества аргументов,
мы избегаем этого путем обработки в цикле каждого файла по очереди.
Поскольку на самом деле мы ищем большое количество разных строк, то мы
хотим видеть имена файлов, в которых они были найдены.
При указании в командной строке grep каталога /dev/null, grep
всегда печатает имя файла до вывода найденной строки в нашем цикле,
поскольку теперь имеется два имени файла в качестве аргументов. Конечно,
в /dev/null ничего нельзя найти, поскольку он пуст по определению.
Последний образец в операторе case (строки 31-36) - это ловушка.
Он соответствует любому количеству позиционных параметров сверх одного,
что позволяет нам иметь переменное число каталогов в командной
строке.
Даже хотя мы можем указать несколько каталогов в командной строке,
первым аргументом по-прежнему является строка поиска. Не так легко
сказать: "начиная со второго параметра", поэтому мы сохраняем строку
поиска (в переменной STRING - Прим. перев.) и убираем ее командой
shift. Теперь мы можем получить доступ к остальной части командной
строки с помощью выражения $@.
Давайте посмотрим, что происходит, когда мы ссылаемся на переменную
$OPT для получения опций команды find. Допустим, мы вызвали tgrep
с опцией -c. Когда мы присваиваем значение переменной OPT, мы ставим
строку c в виде "*.c" внутри двойных кавычек, поскольку мы не желаем,
чтобы shell раскрывал эту строку (т.е. трактовал ее как имена всех
файлов, соответствующих данному образцу) именно сейчас. Теперь, как
только к переменной $OPT есть обращение в команде find, значением OPT
является *.c, что означает поиск файлов, символьные имена которых
соответствуют *.c. Для отмены символьной интерпретации мы должны
использовать команду eval. Перед выполнением команды find команда eval
заставляет shell повторно анализировать команду в отношении распространения
значения переменной. На этом проходе выражение "*.c" превращается
в *.c, что разрешает генерацию имен файлов таким образом, чтобы
были просмотрены все эти файлы.
Указывая $@ в команде find, мы можем производить поиск во всех
каталогах сразу. Таким образом, нам нужно вызвать find только один
раз, что позволяет сберечь время центрального процессора. Одной из интересных
проблем, возникших при разработке данного командного файла
было то, что выражение вида $* не работало. В команде find возникала
ошибка сохранения. Даже запуск shell'а в режиме -x (установленный флаг
разрешения выполнения) не разрешил проблему. Изменение синтаксиса, кажется,
помогло разобраться с ней. Оказывается, причина в том, что выражение
$* раскрывается в "$1 $2 ...", в то время как выражение $@
превращается в "$1" "$2" (т.е. в отдельные аргументы). Происходило то,
что выражение $* передавало имена нескольких каталогов команде find
как одну строку. Команда find не могла обнаружить файл такого типа,
поэтому прекращала выполнение. Когда же вместо этого было использовано
выражение $@, команда find получила несколько независимых строк с именами.
Это вполне подошло команде find, и все заработало. Такие мелкие
детали всегда требуют много времени для изучения!
ВОЗМОЖНЫЕ ИССЛЕДОВАНИЯ
В чем разница между двумя следующими операторами?
grep "$1" `find "$2" -print`
и
find "$2" -print | while read F
do
grep "$1" $F
done
Они кажутся совершенно похожими, но существует различие в главном.
Первый оператор - это один вызов команды grep. Аргументами являются
множество имен файлов, поставляемых командой find. Если find сгенерирует
слишком много имен файлов, то выполнение команды завершится.
О том, что сгенерировано слишком много имен файлов, никакого предупреждения
не выдается, но большое количество файлов фатально для grep.
Поэтому мы должны рассматривать такой синтаксис как недопустимый в общем
случае.
Второй оператор - это цикл. Он работает медленнее и вызывает grep
много раз, что забирает много времени центрального процессора. Однако
положительным моментом является то, что цикл получает данные по конвейеру,
который фактически не имеет ограничений на число данных, которое
он может иметь. Наша программа никогда неожиданно не прекратит выполнение.
2.1.4. paths - нахождение пути доступа к исполняемым файлам, со
специальными опциями

ИМЯ: paths

paths Определитель маршрутных имен файлов со специальными
опциями
НАЗНАЧЕНИЕ
Выводит на экран каталог, в котором располагается файл, выдает
имя файла в длинном формате или ищет файлы с установленным битом пользовательского
идентификатора (setuid bit files) в каталогах по указанному
маршруту.
ФОРМАТ ВЫЗОВА
paths [-l] [-s] file [file ...]
ПРИМЕР ВЫЗОВА
$ paths -l ed ex vi
Выдает в длинном формате имена файлов, которые являются исполняемыми
модулями редакторов ed, ex и vi
ТЕКСТ ПРОГРАММЫ
1 :
2 # @(#) paths v1.0 Path locator with special options Author: Russ Sage
2а Определитель местонахождения файлов со специальными опциями
4 FORMAT="path"
6 for ARG in $@
7 do
8 if [ '`echo $ARG | cut -c1`" = "-" ]
9 then case $ARG in
10 -l) FORMAT="ls"
11 shift;;
12 -s) FORMAT="set"
13 set "1";;
14 *) echo $0: arg error" "&2
15 echo "usage: $0 [-l] [-s] file [file ...]" "&2
16 exit 1;;
17 esac
18 fi
19 done
21 IFS="${IFS}:"
23 for FILE in $@
24 do
25 for DIR in $PATH
26 do
27 case $FORMAT in
28 path) if [ -f $DIR/$FILE ]
29 then echo $DIR/$FILE
30 fi;;
31 ls) if [ -f $DIR/$FILE ]
32 then ls -l $DIR/$FILE
33 fi;;
34 set) echo "\n:::::::::::::::::::"
35 echo "$DIR"
36 echo "::::::::::::::::::::"
37 ls -al $DIR | grep "^[^ ]*s[^ ]*";;
38 esac
39 done
40 done
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ
ARG Содержит каждый аргумент командной строки
DIR Элемент с именем каталога в переменной PATH
FILE Содержит имя каждого файла в командной строке
FORMAT Тип требуемого формата выходных данных
IFS Переменная shell'а, разделитель полей
PATH Переменная shell'а, пути к каталогам исполняемых
модулей
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ paths?
В нашей среде интерпретатора shell переменная с именем PATH содержит
имена каталогов, отделенные друг от друга символами двоеточия
(:). Каждый раз, когда вы вводите команду после приглашения shell'а,
интерпретатор shell, начиная с первого каталога, указанного в переменной
PATH, смотрит, находится ли введенная вами команда в этом каталоге.
Если да, то команда выполняется. Если нет, то shell идет в следующий
каталог, указываемый переменной PATH, и так далее, пока не будут
проверены все каталоги. Если команда все же не будет найдена, то вы
получите следующее сообщение об ошибке:

|
| $ whatchamacallit
| sh: whatchamacallit: not found
|
|
Такой поиск команды осуществляется автоматически, но сама система
не сообщает вам, ГДЕ размещена команда. Нам необходима утилита, которая
ищет и выводит на экран маршрут к указанному файлу. Имея такую
утилиту, вы можете использовать кратчайшие пути получения того, что
вам нужно, и выполнять множество различных трюков. Вы можете комбинировать
подобную команду с другими командами для создания гораздо более
мощных средств. С маршрутом можно также комбинировать команды more,
ls, file и cd системы UNIX. Возможно, вы обнаружите и другие команды
по мере экспериментирования.
Команда, несколько похожая на ту, которую мы ищем, существует
где-то в мире системы UNIX Systev V. Например, в системе AT&T это команда
where. В системе UNIX Berkeley это команда which (текст на языке
Си-shell'а) или whereis (исполняемая программа). Whereis дает дополнительную
информацию, такую как место размещения файлов с исходными
текстами (в каталоге /usr/src). Увидев, как мы создаем нашу собственную
команду поиска маршрута, вы можете модифицировать ее для обеспечения
работы с особенностями некоторых других команд и приспособить такие
вещи к вашим нуждам.
Прежде чем мы удовлетворим свои прихоти, давайте бегло глянем на
команду path, более простую, чем paths. Вся программа выглядит примерно
так:
IFS="${IFS}:"
for FILE in $@
do
for DIR in $PATH
do
if [ -f $DIR/$FILE ]
then echo $DIR/$FILE
fi
done
done
Основная идея очень проста. Сперва мы добавляем двоеточие (:) к
разделителю полей. Нам необходимо сохранить значения, принятые по
умолчанию (пробелы, табуляции, символы новой строки), так, чтобы мы
могли все-таки обрабатывать командную строку с пробелами в качестве
символов-разделителей. Символ : дает нам возможность отдельно рассматривать
каждый маршрут, хранимый в переменной PATH.
Вся программа представляет собой два цикла for. Внешний цикл
просматривает имена всех файлов, указанных в командной строке. Внутренний
цикл последовательно обходит все каталоги, содержащиеся в переменной
PATH. Для каждого файла просматриваются все каталоги с целью
определения, содержит ли этот каталог файл с таким именем. Полное
маршрутное имя представляет собой комбинацию префикса-каталога и имени
файла (называемых именем каталога и базовым именем соответственно).
Встроенная shell-команда test использована для определения того, существует
ли файл в определенном каталоге.
Если ваша переменная PATH выглядит так:
PATH=.:/bin:/usr/bin:/etc/:$HOME/bin
то внутренний цикл выполнит пять итераций в таком порядке: ., /bin,
/usr/bin, /etc и, наконец, $HOME/bin. Если бы запрос имел вид "path
ll" для поиска утилиты, которую мы создадим позже в этой главе, то результат
мог бы выглядеть так:

|
| /usr/bin/ll
| /usr/russ/bin/ll
|
|
Это значит, что команда ll была найдена в двух местах из вашего набора
маршрутов поиска.
ЧТО ДЕЛАЕТ paths?
Теперь, когда мы знаем, как работает более простая команда path,
мы можем по достоинству оценить дополнительные возможности специальной
команды получения маршрута - команды paths. Paths имеет три основные
функции. Она может выполняться как основная команда path, которую мы
уже рассмотрели, и давать полное маршрутное имя исполняемого модуля.
Она может выдавать маршрут файла в длинном формате. Она также может
выдать список всех файлов с установленным пользовательским идентификатором
(setuid bit files), которые есть в ваших маршрутных каталогах.
(См. главу 8, где описаны биты setuid.)
Синтаксис вызова немного отличается для разных опций. Для того
чтобы использовать формат выдачи маршрута или формат команды ls, нужна
такая командная строка:
paths [-l] file [file ...]
как, например, в командах "paths ls who date" или "paths -l ll". Для
поиска файлов с установленным пользовательским идентификатором (setuid
files) не нужно указывать имена файлов в командной строке. Вся команда
должна быть такой:
paths -s
Формат setuid и форматы выдачи маршрута являются взаимоисключающими,
поскольку предполагается, что вы хотите узнать от компьютера,
какие и где находятся файлы, а не отгадывать имена файлов.
Если в командной строке находится какая-то другая опция, то в
стандартный вывод выводится сообщение об ошибке и командный файл завершается.
Опции устанавливают флаг формата вывода в одно из трех значений.
Весь дальнейший вывод из командного файла управляется выбранным
форматом вывода.
ПРИМЕРЫ
1. $ paths ls more who paths
/bin/ls
/usr/bin/more
/bin/who
/usr/russ/bin/paths
Поиск маршрутов к командам ls, more, who, paths. При выводе указываются
полные абсолютные маршрутные имена. Обратите внимание, что в
конце имени каждого файла печатается символ новой строки, чтобы получить
распечатку, в которой каждое имя файла стоит в отдельной строке.
2. $ more `paths gettydefs termcap paths`
Если ваша переменная PATH содержит каталог /etc, то этот пример
будет работать. Если нет, то первые два файла не будут найдены. Сначала
запускается команда paths, и ее вывод помещается на свое место в
командной строке команды more. Когда запускается команда more, она не
знает, что ее аргументы получены от другой команды. После завершения
работы команды paths команда more принимает вид:
more /etc/gettydefs /etc/termcap /usr/russ/bin/paths
с полными маршрутными именами каждого файла. Этот пример показывает,
как можно заставить команду paths выполнять всю работу по поиску и показу
файлов, которые вы хотите увидеть.
3. $ ll `paths ll`
В этом примере в длинном формате выводятся файлы с именами ll,
которые найдет path. (Мы представим нашу версию команды ll несколько
позже в этой же главе.) Как и в предыдущем случае, сначала генерируется
информация о маршруте, затем она помещается в командную строку, а
затем запускается команда ll.
4. $ m `paths paths`
В данном примере генерируется маршрутное имя самого командного
файла paths и передается программе m, которая использует команду more
для распечатки. (Командный файл m мы также покажем вам позже.)
ПОЯСНЕНИЯ
В строке 4 инициализируется переменная FORMAT, указывая маршрутный
тип поиска. Выполняется действие по умолчанию, точно такое же, как
в командном файле path, который мы рассмотрели ранее.
В строках 6-19 все аргументы командной строки проверяются на корректность.
Критерием того, что аргумент есть опция, является дефис в
роли первого символа. Заметим, что здесь не разрешено использование
синтаксиса "-xyz". Это заставляет вас пользоваться синтаксисом "-x -y
-z". Хотя этот момент может показаться несущественным, на самом деле
он важен. Всегда нужно достигать компромисса между быстрой разработкой
командного файла при согласии на недостатки жесткого синтаксиса - и
разрешением гибкого формата за счет дополнительных усилий по кодированию
и отладке и за счет более медленного выполнения. Ваш выбор зависит
от ваших приоритетов, от количества людей, использующих ваше инструментальное
средство, и от того, насколько критична скорость выполнения.
Конечно, если скорость критична, вы, вероятно, захотите использовать
каким-то образом язык Си. Мы оставляем обработку конкатенированных
опций в качестве упражнения для читателя.
Цикл for проходит по всем позиционным параметрам. Если первым
символом аргумента является "-", то он сверяется со списком допустимых
аргументов с помощью оператора case в строках 9-17. Опция "-l" изменяет
переменную формата, после чего убирается из рассмотрения. Это делается
для освобождения этой позиции, чтобы конечным результатом были
просто имена файлов в командной строке.
Опция "-s" также изменяет переменную формата. Однако, вместо того,
чтобы убрать опцию из командной строки, она ликвидирует всю командную
строку и заменяет ее символом "l". Это заставляет цикл for
проходить только одну итерацию, так как в командной строке теперь
только один параметр. Благодаря такому обращению с командной строкой,
нам не нужен другой цикл: мы можем использовать тот же цикл, что и в
определении маршрута, без всяких модификаций. Поскольку после опции s
не ожидается никаких имен файлов, мы больше не хотим рассматривать командную
строку.
Если использована опция, которая не является ни l, ни s, то этой
опции соответствует звездочка (*) и в стандартный файл ошибок выводится
сообщение об ошибке. Затем командный файл завершается.
Мы бы могли просто проверить первый параметр командной строки,
чтобы выяснить, является ли он опцией, и если является, то установить
эту опцию. Поскольку можно использовать только одну опцию за один раз,
мы могли бы предполагать, что в остальной части командной строки были
имена файлов. Тем не менее, этот цикл допускает простое добавление
других опций, которые могли бы действовать в дополнение к одной основной.
Это более предпочтительно, и оно не влияет на производительность.
В строке 21 мы добавляем символ двоеточия (:) к другим символам
разделителя полей. Мы должны именно добавить двоеточие, а не превратить
разделитель полей только в двоеточие. Если бы мы сделали последнее,
то это запутало бы разбор имен файлов в командной строке.
Основной цикл представлен в строках 23-40. Это двойной цикл for.
Внешний цикл проходит по каждому файлу в командной строке, а внутренний
цикл обрабатывает каждый каталог, указанный в вашей переменной
PATH. Обратите внимание, что внешний цикл идет по именам файлов, а не
по записям каталогов. Если бы мы выбрали второе, то в распечатке нарушился
бы порядок имен файлов, поскольку поиск шел бы сначала по каталогам.
Следовательно, для каждого имени файла и каталога действие зависит
от требуемого формата. Маршрутный формат печатает полное имя,
листинговый формат выполняет команду ls, а формат set не ищет указанные
имена файлов, но проверяет права доступа и ищет файлы с установленным
пользовательским идентификатором.
Обрат ПРИЕМЫ ПРОФЕССИОНАЛЬНОЙ РАБОТЫ В UNIX
аналогами. Опция ls есть дополнение, которое сокращает объем работы
при вызове. Наличие комбинации поиска и команды ls освобождает того,
кто вызывает этот командный файл от необходимости применять команду
подстановки. Старая и новая команды выглядят примерно так:
ll `path ll`
Находит путь к ll, а затем запускает на нем команду ls -l.
paths -l ll
Находит путь и вместо того, чтобы его напечатать,
выполняет команду ls -l применительно к этому пути.
Формат setuid в строке 34 прощается с подходом "один файл за один
раз" и включает каталоговую машину. Поскольку внешний цикл установлен
на одну итерацию, внутренний цикл становится главным. Для каждого каталога,
указанного в PATH, печатаются оформление из двоеточий и имя
каталога. Это делает распечатку приятной, информативной и наглядной.
Ключевой командой является комбинация ls-grep. Каждое имя файла в
каталоге распечатывается в длинном формате, затем просматривается бит
установки пользовательского идентификатора. Модель такова, что команда
ls -al $DIR печатает следующее:

|
| -rws--x--x 1 root bin 16235 Sep 13 1985 /bin/su
|
|
Аргумент "^[^ ]*s[^ ]*" означает поиск от начала строки символа,
отличного от пробела, за которым следует один или более символов, отличных
от пробела, затем символ s и затем один или более символов, отличных
от пробела. Это выражение ограничивает поиск битами прав доступа
в начале строки. Если имеется символ s где-либо в правах доступа
(либо в пользовательском идентификаторе процесса, либо в групповом
идентификаторе процесса), то команда grep отрабатывает успешно и печатается
вся строка.
Такой вид поиска установленного пользовательского идентификатора
несколько "легковесен" в том смысле, что поиск ведется только согласно
переменной PATH, которая у вас есть. Файлы с установленным пользовательским
идентификатором могут находиться в каталогах, которые не указаны
в PATH. Однако в такой реализации данная опция обеспечивает быстрое
обращение к вашим локальным файлам с установленным пользовательским
идентификатором.
ВОЗМОЖНЫЕ МОДИФИКАЦИИ
Данный командный файл открыт для многих различных видов модификации.
Поиск полного имени файла является фундаментальной задачей программного
обеспечения по сопровождению файлов. Эта возможность позволяет
нам полагаться на саму программу paths или использовать paths в качестве
куска более объемной программы.
При разработке ваших собственных программ следует обратить внимание
на гибкость командного файла paths, которая выражается в отличии
между обрабатываемыми форматами. Первые два формата используют отдельные
файлы, а формат set использует каталоги. Дальнейшие дополнения к
командному файлу paths могут касаться любой из этих строк или могут
комбинировать их. Если есть необходимость, программное обеспечение может
приспособиться к этому.
2.2. ВЫВОД ИНФОРМАЦИИ
2.2.1. lc - вывод файловой информации на экран по столбцам

ИМЯ: lc

lc Выдает список файлов в колоночном формате
НАЗНАЧЕНИЕ
Выдает информацию о файлах в формате колонок, показывая каталоги
и исполняемые модули. Этот листинг можно пропустить через команду
more.
ФОРМАТ ВЫЗОВА
lc [-m] [ls options] file [file ...]
ПРИМЕР ВЫЗОВА
lc -R $HOME
Выдает список всех файлов во всех подкаталогах моего регистрационного
каталога.
ТЕКСТ ПРОГРАММЫ
1 :
2 # @(#) lc v1.0 List files in a column Author: Russ Sage
2а Выводит список файлов в колоночном виде
4 if [ "$1" = "-m" ]
5 then MORE="| /usr/bin/more"
6 shift
7 else MORE=""
8 fi
10 eval "/bin/ls -a $@ | /bin/pr -5t" $MORE # pre System V
11 eval /bin/ls -aCF $@ $MORE # System V
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ
MORE Содержит программный канал к команде more
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ lc?
В мире компьютеров многие люди изобретают колесо, а другие люди
изобретают его снова. Если первое колесо не того размера или не того
цвета, делается другое колесо. В нашей конкретной ситуации исходным
колесом является команда ls системы UNIX, которая имеет некоторые недостатки
в своих ранних реализациях. Она выдает хорошую информацию, но
она печатает имена файлов только в одну колонку, что приводит к нерациональному
расходованию места и затрудняет чтение имен файлов. Поэтому
мы создаем версию команды ls, которая отображает распечатки в
несколько колонок.
Как видно из предыдущего листинга, lc имеет две формы. Одна предназначена
для систем, более ранних, чем System V, а другая - для
System V и последующих версий UNIX. Причина в том, что System V версии
2 имеет новую команду ls, которая делает именно то, что мы хотим.
Система Berkeley также имеет версию команды ls, которая по умолчанию
использует несколько колонок при выводе на терминал. Но для XENIX и
ранних версий System V мы должны делать это сами. Дело в том, что хотя
в вашей версии UNIX, XENIX или чего-либо еще могут отсутствовать команды,
имеющиеся в других версиях, вы обычно можете построить то, что
вам нужно. Это может потребовать определенных усилий, и ваши программы
могут работать не так быстро и не так эффективно, но вы МОЖЕТЕ получить
нужное средство.
Пользователям интерпретаторов csh и последнего sh, имеющего функции,
видимо, лучше бы заменить весь этот сценарий на то, чтобы сделать
lc псевдонимом (alias). Использовать возможность введения псевдонимов,
чтобы присвоить имя любой корректной командной строке UNIX (например,
вызову команды ls с указанными опциями). Это легче, чем писать командный
файл, но ограничивает вас необходимостью работать с уже имеющимися
командами или опциями. Это быстрее, так как не создается никаких дополнительных
процессов.
При работе со старым интерпретатором sh мы должны пройти через
обычную процедуру изготовления командного файла и размещения его в каталоге
bin. С другой стороны, SCO XENIX System V решает эту проблему,
связывая эти же имена (lc, lf, l) с обычной командной ls и используя
вызывающее имя для определения формы распечатки.
Итак, зачастую имеется много альтернатив. Мастера UNIX, сталкиваясь
с какой-либо проблемой, не борются с ней с помощью Си или командного
файла интерпретатора shell. Поскольку они знакомы с существующими
ресурсами системы UNIX, они могут рассмотреть проблему и выбрать стратегию,
использующую наименее сложное средство, выполняющее данную работу
с приемлемым уровнем производительности. В порядке возрастания
сложности, это могут быть непонятная, но существующая команда и/или
опция, псевдоним, командный файл интерпретатора shell или программа на
языке Си.
ЧТО ДЕЛАЕТ lc?
Общий подход к разработке этой команды заключается в том, чтобы
собрать вместе некоторые опции и сделать новую команду с более мощным
интерфейсом. Чтобы достичь этой мощи, мы можем сделать пре- или постпроцессор
для обычной команды системы UNIX.
Главная задача здесь - печать колонок, поэтому мы смотрим на опции
команды ls, чтобы задействовать их. Конечно, мы включаем опцию -C.
Какие еще опции ls нам нужны? Обычно UNIX не печатает файлы, имена которых
начинаются с точек, например, .profile, если только вы не указываете
ls -a. Это забывается при просмотре этих важных файлов, поэтому
мы конструируем нашу команду так, чтобы она печатала их по умолчанию.
Никакие файлы не скрываются от нас. Для пользователей System V и BSD
(или для любого, кто имеет опцию -F), листинг улучшается за счет вывода
"/" после имени каталога и "*" после исполняемого файла. Ранняя команда
ls системы UNIX не имела возможности печатать в таком стиле. Отметим,
что данное использование термина "исполняемый" означает показ
того, что флаги прав доступа имеют бит "x", а не то, что это файл типа
a.out с магическим числом. Это отличие важно тем, что делает наш командный
файл более полезным.
Если ожидается длинная распечатка, как это бывает обычно для рекурсивных
каталогов, то вы хотите иметь доступ к команде more. Мы
встраиваем команду more так, чтобы ее можно было активировать с помощью
опции -m. Опция -m должна быть первой опцией после имени команды,
из-за способа, которым она проверяется внутри программы. Если она
передается после первой опции, она переходит к команде UNIX ls и интерпретируется
как печать в потоковом формате. Это такой формат, в котором
все имена расположены в строках, разделенных запятыми (,). Как
мы уже отмечали, вы можете сделать интерфейс этого командного файла
более гибким за счет дополнительной работы над ним.
ПРИМЕРЫ
1. $ lc `path lc`
Получает полное имя для lc и распечатывает файловую информацию в
виде колонок.
2. $ lc -m -R /
Печатает колоночный список ВСЕХ файлов в системе, рекурсивно проходя
вниз по иерархии системного дерева и пропуская распечатку через
команду more.
Еще один маленький фокус: этот синтаксис был использован для создания
другой команды, названной expose. Командная строка "lc -m -R $@"
давала бы рекурсивный список всех файлов в любом каталоге по вашему
выбору в приятном постраничном формате.
3. $ lc -m -R /usr/lib
Рекурсивно распечатывает список всех файлов во всех каталогах,
начиная с /usr/lib, и пропускает листинг через команду more.
4. $ lc -m . | more
Выдает список файлов в текущем каталоге и пропускает листинг через
команду more, а затем снова пропускает все через more. Работает ли
это ? Никоим образом. Возникает полная путаница, и клавиша прерывания
обычно является наилучшим способом выхода из данной ситуации.
ПОЯСНЕНИЯ
В строках 4-8 проверяется, является ли первым аргументом командной
строки -m - опция команды more. Если эта опция найдена, то в переменную
MORE заносится указание конвейера и команда more. Тем самым
устанавливается постобработка, которую следует применить к выходу команды
ls. Затем эта опция убирается из командной строки. Это делается
для того, чтобы остаток командной строки можно было передать команде
ls, не вызвав при этом нежелательных эффектов. Если первой опцией не
является -m, переменной MORE присваивается нулевое значение, чтобы она
впоследствии не влияла на командную строку.
Строка 10 - это командная строка, которую вы бы использовали на
старой UNIX-машине типа Version 7 или System III. Она не имеет ни
встроенной опции для печати символов косой черты (/) и звездочек (*),
ни возможности печати в виде колонок. Вы должны пожертвовать первой
возможностью, а распечатки в виде нескольких колонок можно получить с
помощью команды pr системы UNIX. Команда pr использована с опцией
"-5t", поэтому она печатает в пять колонок (что обычно приемлемо, но
если встречаются длинные имена файлов, то пяти колонок может оказаться
слишком много) и не печатает верхний и нижний колонтитулы. Благодаря
отказу от колонтитулов, 24-строчный формат не слишком неудобен для
вас.
Отметим, что здесь использована команда eval. Это специальная
встроенная команда интерпретатора shell, которая выполняет перевычисление
текущей строки, подлежащей выполнению. Интерпретатор shell
повторно анализирует эту строку, чтобы раскрыть значение имен переменных
в командной строке и обеспечить распознавание переменных как таковых.
Здесь мы перевычисляем переменную MORE. Напомним, что мы поместили
в эту переменную конвейер. Если мы не перевычислим командную строку,
то команда pr попытается открыть файлы "|" и "more", которые не
существуют. Для того, чтобы shell вместо этого воспринял эти символы
как указания конвейеров и программ, и используется команда eval.
Строка 10 имеет еще одну особенность. В командной строке уже есть
один конвейер. Откуда shell знает, трактовать ли символ "|" как имя
файла или как конвейер? Благодаря тому, что аргумент команды eval заключен
в кавычки. Это указывает команде eval сохранить все, что находится
в кавычках, без изменений, но раскрыть значение переменной MORE
и поместить его в конец командной строки, находящейся в кавычках.
Несколько непонятно, но если вы думаете об этом пару лет, оно становится
осмысленным.
Для тех из вас, кто имеет новую команду ls (System V, версия 2
или BSD 4.2), не требуется два конвейера в команде. Как показывает
строка 11, мы получаем подходящий колоночный формат из самой команды
ls, вместе с показом всех файлов и специальными символами / и * для
каталогов и исполняемых файлов. Обозначение $@ относится ко всему содержимому
командной строки, т.е. к вашим дополнительным опциям команды
ls и к именам файлов, список которых вы хотите распечатать. Поступая
таким образом, мы создаем фундамент (опции a,C,F), а вы можете
надстраивать его (используя опции R,t и т.д.). Скромный, но элегантный
фокус заставить ls сообщить свои опции заключается в том, чтобы вызвать
ее с неверной опцией. Большинство команд не используют опции z
или ?, поэтому вызов "ls -z" или "ls -?" приведет к такому результату:

|
| ls: illegal option — z
| usage: -1ACFRabcdfgilmnopqrstux [files]
|
Все эти опции представляют определенный интерес. Если вы часто
используете какие-либо из них, поместите их в командный файл lc, и вы
получите вашу собственную адаптированную команду.
Вы обратили внимание, что все обычные команды системы UNIX,
используемые в нашем командном файле, имеют полные маршрутные имена?
Это может показаться несколько странным, но причина указания полных
маршрутных имен в том, что когда shell запускает команду, он не должен
возвращаться к анализу переменной PATH и искать, где расположена команда.
Если вы обращаетесь к командам относительным способом, время
поиска файлов представляет собой большие накладные расходы. Когда вы
вызываете lc, интерпретатор shell ищет эту команду, затем lc вызывает
ls, которую тоже нужно найти. Если после этого результаты пропускаются
через more или pr, то требуется дополнительный поиск. А полные маршрутные
имена распознаются интерпретатором shell сразу же (он видит,
что первым символом является /), и нужная команда может быть вызвана
быстро. Издержки на поиск - единственные издержки команды lc.
Использование полных имен, естественно, требует, чтобы вы знали,
где в системе размещены утилиты, к которым вы хотите обратиться. Вы
можете применить команду paths, чтобы получить корректные полные имена
для жесткого указания их в тексте вашего командного файла, а можете
переписать данный командный файл при переходе в другую систему. Это
просто еще одна иллюстрация универсального компромисса между скоростью
и эффективностью, с одной стороны, и гибкостью и мобильностью, с другой.
2.2.2. ll - вывод файловой информации в длинном формате

ИМЯ: ll

ll Выдает список файлов в длинном формате
НАЗНАЧЕНИЕ
Выдает список файлов в длинном формате (-l). Распечатку можно
пропустить через команду more.
ФОРМАТ ВЫЗОВА
ll [-m] [ls options] file [file...]
ПРИМЕР ВЫЗОВА
ll *.c Выдача списка файлов с исходными текстами на
языке Си в длинном формате.
ТЕКСТ ПРОГРАММЫ
1 :
2 # @(#) ll v1.0 Long listing of files Author: Russ Sage
2а Выводит список файлов в длинном формате
4 if [ "$1" = "-m" ]
5 then MORE="| /usr/bin/more"
6 shift
7 else MORE=""
8 fi
10 eval /bin/ls -al $@ MORE
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ
MORE Содержит строку передачи результатов по
конвейеру команде more
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ ll?
Мотивы для создания команды ll те же, что мы обсудили ранее для
команды lc. Мы можем использовать ll для нескольких целей. Она уменьшает
количество требуемых нажатий на клавиши, позволяет избежать необходимости
помнить специальные опции и вообще настраивает систему соответственно
нашим требованиям вместо того чтобы нам приспосабливаться к
системе.
ЧТО ДЕЛАЕТ ll?
Основой этой команды является известная команда "ls -l". Она,
если вы помните, дает очень емкую информацию о каждом файле, включая
права доступа, связи, имя владельца, размер и так далее. (Кстати,
программисты, использующие язык Си, могут получить эту информацию при
помощи системного вызова stat(2).) Поскольку такой список при наличии
множества файлов может легко переполнить экран, то предоставляется опция
-m. Она обеспечивает постраничный вывод с помощью команды more.
Отметим, что если используется эта опция, то она должна стоять первой.
Строка символов, соответствующая этой опции, удаляется командой shift,
так что она не смешивается с именами файлов и обычными опциями команды
ls, которые передаются как аргументы.
После опции -m (если она есть) ll допускает указание любых других
допустимых опций команды ls. Можно также использовать любую комбинацию
имен файлов. Здесь применимы обычные средства порождения имен файлов:
* соответствует любым символам, ? - одному символу, а символы [] задают
диапазон из некоторого набора символов. В итоге мы получили команду
ls, которая по умолчанию работает как "ls -l", вызывает команду more с
помощью одной опции вместо необходимости указания конвейера в командной
строке и при этом сохраняет гибкость команды ls.
ПРИМЕРЫ
1. $ ll /etc/*mount*
Выводит список всех файлов в каталоге /etc, имена которых содержат
в каком-либо месте слово mount (например, mount, umount,
unmountable).
2. $ ll -i `who|awk '{print "/dev/" $2}'`
Сперва выполняется команда who, затем результат ее работы по конвейеру
передается команде awk, которая вырезает имя устройства и приписывает
ему префикс /dev/. В результате список полных маршрутных имен
ко всем терминальным устройствам, зарегистрированным в настоящий момент,
помещается в командную строку команды ls -li. В распечатке указана
вся информация об индексном дескрипторе файла (inode) для каждого
терминального устройства.
3. $ ll `kind -a /lib`
Выводит в длинном формате список всех файлов архива в каталоге
/lib. Этот каталог содержит библиотеки компиляторов всех языков системы
UNIX. (Команда kind, которая отбирает файлы по их типу, рассматривается
в следующем разделе.)
4. $ ll -m -i /dev
Выводит всю обычную информацию плюс номер индексного дескриптора
для всех файлов в каталоге /dev. Выдача на экран происходит с помощью
команды more.
ПОЯСНЕНИЯ
Если первым позиционным параметром является -m, то в строке 4
инициализируется переменная MORE для подключения конвейера и программы
/usr/bin/more. (Вопрос о том, почему используется абсолютное маршрутное
имя, обсуждался в предыдущем разделе.) Затем символьная строка -m
командой shift убирается из командной строки. Если же первой опцией не
является -m, то переменная MORE устанавливается в нуль, чтобы не влиять
на повторный разбор командной строки, выполняемый с помощью команды
eval (строка 10).
В строке 10 команда eval использована для получения результирующей
командной строки. Команда ls вызывается с опциями -al (выдача
списка всех файлов в длинном формате), которые мы установили по умолчанию.
Затем берутся аргументы командной строки (минус первый аргумент,
если это был -m, который мы убрали командой shift). Этими аргументами
могут быть дополнительные опции команды ls плюс имена файлов
или каталогов. В конце строки значение переменной MORE обеспечивает
конвейер с командой more, если была указана опция -m. В противном случае
значение переменной MORE равно нулю и не оказывает никакого влияния
на анализ содержимого командной строки.
Что произошло бы, если бы пользователь указал опцию -m в качестве
второй (или последующей) опции? В этом случае опция -m передалась бы
команде ls. Команда ls трактовала бы эту опцию как "потоковый вывод",
а это совсем не то, что мы хотели. Однако команда ls была вызвана также
с опцией -l, которая отменяет опцию -m в соответствии с текстом
программы ls. Вы не получили бы вывод с помощью команды more, но ваши
выходные данные по-прежнему были бы выведены в правильном формате.
2.2.3. kind - вывод однотипных файлов

ИМЯ: kind

kind Выдача списка имен файлов определенного вида
НАЗНАЧЕНИЕ
Выбирает и выводит имена всех файлов в указанном каталоге (каталогах),
имеющих указанный тип. Если не указан никакой тип, выбираются
текстовые файлы.
ФОРМАТ ВЫЗОВА
kind [-a] [-d] [-t] [-x] [file...]
ПРИМЕР ВЫЗОВА
more `kind /etc/*`
Вывод командой more всех текстовых файлов, имеющихся в катало-
ге /etc.
ТЕКСТ ПРОГРАММЫ
1 :
2 # @(#) kind v1.0 Prints files of the same kind Author: Russ Sage
2а Выводит список файлов определенного вида
4 if [ $# -gt 0 ]
5 then if [ `echo $1 | cut -c1` = "-" ]
6 then case #1 in
7 -a) KIND='archive'
8 shift;;
9 -d) KIND='data'
10 shift;;
11 -t) KIND='text'
12 shift;;
13 -x) KIND='executable'
14 shift;;
15 *) echo "kind: arg error" "&2
16 echo "usage: kind [-a] [-d] [-t] [-x] [file...]" "&2
17 echo " -a archive" "&2
18 echo " -d data" "&2
19 echo " -t text, default" "&2
20 echo " -x executable" "&2
21 echo " if no args, reads stdin" "&2
22 exit 1;;
23 esac
24 fi
25 fi
27 : ${KIND:='text'}
29 case $# in
30 0) while read FILE
31 do
32 file $FILE | fgrep $KIND | cut -d: -f1
33 done;;
34 *) file $@ | fgrep $KIND | cut -d: -f1;;
35 esac
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ
FILE Содержит имена файлов по мере их чтения из stdin
(стандартного ввода)
KIND Содержит строку, определяющую тип файла
ОПИСАНИЕ
ЗАЧЕМ НУЖЕН КОМАНДНЫЙ ФАЙЛ kind?
Файловая система UNIX имеет строгие стандарты при рассмотрении
файлов. Имеется три вида файлов: обычные файлы (тексты, данные, исполняемые
файлы), каталоги и устройства. Каждый вид файлов имеет свое
предназначение и обычно имеет особые команды или файлы данных, которые
работают с ним.
Давайте рассмотрим, как некоторые из существующих команд системы
UNIX рассматривают типы файлов. Команда ls делает различие между каталогами
и другими файлами, поэтому она может быть полезна. Другой важной
командой является команда file. Она сообщает вам, какой тип имеет
данный файл, что потенциально полезно, но слишком мало. Для того чтобы
извлечь из команды file какую-либо полезную информацию, вы должны
надстроить над ней некоторую программу. Что нам действительно нужно,
так это некий гибрид команд ls и file, т.е. утилита, которая выводит
имена всех файлов указанного типа.
Примером полезности такого рода утилиты может служить анализ содержимого
каталогов. Возьмем, например, каталог /etc. Он содержит
программы, файлы данных и текстовые файлы. Для каждого из этих типов
требуется свой собственный тип анализа. Программы анализируются командами
ls, size, nm и file. Файлы данных анализируются командой od.
Текстовые файлы анализируются командами more, wc, head, tail и другими.
Таким образом, обычно вам необходимо работать в данный момент времени
с файлами какого-нибудь одного типа.
ЧТО ДЕЛАЕТ kind?
Командный файл kind - это утилита, которая распечатывает имена
всех файлов, имеющих указанный тип. Она имеет интерфейс, похожий на
интерфейс команды ls, т.е. вы можете передать ей опции и имена файлов
или символы расширения имен файлов. При выводе отображаются полные
имена, если они были указаны, но вы можете избежать появления лишней
информации при выводе, если предварительно перейдете в нужный каталог
с помощью команды cd. Например, если бы я находился в моем регистрационном
каталоге (/usr/russ) и ввел команду
$ kind -d /etc/*
то вывод мог бы выглядеть так:

|
| /etc/mnttab
| /etc/utmp
| /etc/wtmp
|
То есть, вывелся список всех файлов данных. А если бы я выполнил
такую последовательность команд:
$ cd /etc
$ kind -d *
то при выводе убрался бы маршрут, использованный в вызывающей последовательности,
и напечаталось бы следующее:

|
| mnttab
| utmp
| wtmp
|
Затем выход в таком виде может быть использован во внешней команде
для распечатки и анализа файловой информации.
Допустимыми опциями команды kind являются -a для файлов архивов,
-d для файлов данных, -t для текстовых файлов (что является умолчанием)
и -x для исполняемых файлов. Определение этих типов соответствует
команде UNIX file. Заметим, что критерии того, что файл является
исполняемым, в команде file отличаются от тех, которые применяет команда
ls: ls проверяет биты x в индексном дескрипторе файла, в то время
как file проверяет, являются ли первые несколько байтов содержимого
файла "магическим числом". Это магическое число является идентификатором
структуры a.out (см. /usr/include/a.out.h), который сообщает "Я
являюсь скомпилированной Си-программой".
Имена файлов появляются в командной строке после опций. Эти имена
могут быть порождены любым стандартным методом системы UNIX. Если в
командной строке нет имен файлов, то kind превращается в фильтр и читает
стандартный ввод для получения списка имен файлов. (Обратите внимание,
что я сказал "имен файлов", а не "файлов". Можно использовать
опции, поскольку они убираются из командной строки командой shift по
мере того, как они встречаются.) Таким образом, вы можете использовать
другие команды для того, чтобы передать по конвейеру список файлов
утилите kind. Она отфильтровывает и выводит только те из них, которые
соответствуют нужному вам типу.
ПРИМЕРЫ
1. $ od `kind -d /etc/*`
Выглядит так, как будто это должно работать, но команда od не работает
с набором имен файлов. Она может обрабатывать только один файл
в данный момент времени.
2. $ ll `sh -x kind -a /lib/*` | m
Это длинный пример. Выводит в длинном формате список всех файлов
архивов, которые находятся в каталоге /lib. Мы запускаем shell в отладочном
режиме выполнения, так что вы можете увидеть каждую командную
строку перед ее выполнением. Результат по конвейеру передается команде
more.
3. # find / -print | kind -x | while read FILE
" do
" ll $FILE
" done " /tmp/filelist
Данный цикл обнаруживает все действительно исполняемые файлы. Для
каждого из них выполняется команда "ls -l". Отметим, что здесь команда
ll вызывается для каждого имени файла.
Вы могли бы выполнить ту же операцию при помощи такого оператора
find:
# find / -perm -0111 -exec ll {} \;
но опция perm в данном случае опять же проверяет биты прав доступа в
индексном дескрипторе файла, а не ищет магическое число в структуре
a.out, как описано ранее. Кстати, для того, чтобы вы могли успешно запустить
команду file (и тем самым kind) на системных файлах, вы должны
иметь права чтения, чтобы можно было прочитать магическое число.
4. $ for F in `kind /bin/* /usr/bin/* /etc/*`
" do
" fgrep "trap" $F /dev/null
" done
$ fgrep "trap" `kind /bin/* /usr/bin/* /etc/*`
$ find /bin /usr/bin /etc -exec fgrep "trap" {} \;
Это три различных способа поиска слова "trap" во всех текстовых
файлах.
ПОЯСНЕНИЯ
Опции, которые могут быть указаны в командной строке, должны быть
самым первым аргументом. Это создает более строгий синтаксис, по которому
можно выловить ошибку. Но это несколько ограничивает гибкость.
Как было ранее отмечено, вы можете, если хотите, по-своему разрешить
компромисс между эффективностью и гибкостью путем дополнительного
программирования.
В строке 4 проверяется, включены ли какие-либо параметры. Если
параметры есть, то они обрабатываются. Если не используются никакие
параметры, ничего не делается и управление передается на строку 27.
Если были использованы какие-либо аргументы и первым символом является
знак минуса (-), то выполняется оператор case для определения
того, какой тип файла указан. Переменная KIND устанавливается в соответствии
с типом файла, и данный параметр удаляется из командной строки
командой shift. Если аргумент не совпадает ни с одной из допустимых
опций, то ему соответствует случай *, что означает ошибку. На стандартное
устройство регистрации ошибок выводится соответствующее сообщение
об ошибке и синтаксическая подсказка, после этого kind завершается
с плохим статусом выполнения.
В строке 27 производится проверка того, установлена переменная
KIND или равна нулю. Если она равна нулю, в нее подставляется символьная
строка "text". Если KIND уже установлена, то она не меняется. Это
неплохой оператор присвоения значения по умолчанию. Таким образом,
пользователь не обязан указывать опцию -t в командной строке. Если же
опция -t была указана, то ей есть что сопоставить в операторе case.
Оставшаяся часть программы в строках 29-35 представляет собой еще
один оператор case, который проверяет количество аргументов, оставшихся
в командной строке после обработки ошибок. Если была указана какая-либо
опция, то переменная KIND установлена и опция убрана командой
shift. В командной строке могли остаться только аргументы, которые являются
именами файлов или маршрутами. Если к тому времени, когда мы
уже готовы к заключительной обработке, не осталось никаких аргументов,
то значит в командной строке не было указано ни одного имени файла.
В этом случае в строках 30-33 организовывается цикл, который читает
имена файлов из стандартного ввода, запускает команду file и
использует команду fgrep для определения того, соответствует ли тип
файла, выданный командой file, интересующему нас типу (хранимому в переменной
KIND). Затем мы используем команду cut для выделения того,
что нам нужно. Обычный вывод команды file содержит имя файла, двоеточие
и затем описание. Нам нужно только имя файла, поэтому мы вырезаем
первое поле, используя разделитель ":". Когда никакие данные больше не
поступают, цикл while завершается, мы попадаем в конец оператора case
и выходим из программы.
Если же аргументы НАЙДЕНЫ в командной строке, то вместо всего
этого выполняется ветвь оператора case в строке 34. С помощью обозначения
$@, имена всех файлов в командной строке включены в команду
file. Таким образом, не нужен никакой цикл. Во всем остальном обработка
идентична строке 32.
ВОЗМОЖНЫЕ МОДИФИКАЦИИ
Было бы неплохо, если бы командный файл kind мог работать одновременно
с разными типами файлов. Это означает наличие несколько опций
в командной строке, например -a и -d. Вам могла бы понадобиться
составная строка, в которой каждая часть была бы отделена символом |.
Затем эта строка могла бы быть использована в команде egrep, например,
"egrep 'archive|data'". Вам пришлось бы организовать цикл по командной
строке вместо использования фиксированных позиций и убедиться в том,
что вы не получите зациклившийся конвейер, когда задана только одна
опция.
2.2.4. m - простой доступ к команде more

ИМЯ: m

m Простой доступ к команде more
НАЗНАЧЕНИЕ
Обеспечивает быстрый и простой способ постраничного вывода
ФОРМАТ ВЫЗОВА
m [more options] [file ...]
ПРИМЕР ВЫЗОВА
m * Вывод командой more всех файлов текущего каталога
ТЕКСТ ПРОГРАММЫ
1 :
2 # @(#) m v1.0 Easy access to more
2а Простой доступ к команде more
4 /usr/bin/more $@
ОПИСАНИЕ
ЗАЧЕМ НУЖЕН КОМАНДНЫЙ ФАЙЛ m?
Система UNIX сильно загромождается по мере своего функционирования.
В ней обычно имеется множество текстов и данных. Просмотр громадного
количества данных требует многократного нажатия на клавиши, если
вы должны вручную управлять постраничным выводом или периодически вызывать
команду more. Нам необходимы программные средства, которые помогут
нам ускорить эту работу. Одним из таких средств является m. Оно
очень короткое и простое, но это не значит, что оно бесполезно.
Имеется два основных способа вывода данных на экран. Первый
способ - непосредственный вызов команды, например, "more datafile". Вы
направляете данные на экран самой командой. Второй способ - использовать
какую-нибудь команду для получения данных, а затем в конце перехватить
их командой more, например "od -c . | more". В обоих этих случаях
мы вводим с клавиатуры много символов. Сделав так, чтобы команда
more вызывалась по одному символу, мы могли бы уменьшить последние две
команды на шесть нажатий на клавиши. За целый день это хоть немного
предохранит клавиатуру от разрушения! (Если ваша система поддерживает
вызов команд по псевдонимам (aliasing), то, как указывалось ранее, вы
могли бы использовать в этом случае команду alias: "alias m more".)
ЧТО ДЕЛАЕТ m?
Надеемся, все ваши системы имеют команду more или хотя бы ее замену.
Постраничный вывод имеет важное значение при работе с текстом
большого объема.
Все опции и аргументы передаются в командной строке. Вы можете
указать опции команды more в командной строке команды m. Они передаются
без изменений. Можно указать имена файлов. Если они указаны, команда
more выводит их. В противном случае ожидается поступление данных
со стандартного ввода. Таким образом, m может быть использована в качестве
"перехватчика" или как фильтр, как и команда more.
Для тех, кто не слишком знаком с опциями команды more, отметим,
что существуют две изящные возможности: 1) вход в редактор vi в том
месте, где находится курсор при выводе командой more; 2) выход из more
для запуска команды shell и возврат в то место, откуда вы вышли. Первая
опция выполняется при нажатии клавиши "v" в строке состояния команды
more. (То есть когда more отобразила полный экран текста и ждет
продолжения.) Вторая опция запускается при вводе ":!cmd" или "!cmd".
Когда команда выполнится, more вернется в то же место. Как видите, это
синтаксис командной строки ex. Команда more в самом деле имеет небольшую
часть редактора ex, спрятанную внутри нее. Вы можете выполнить
многие команды редактора, указывая их после подсказки в строке состояния
команды more.
Обычный сеанс работы выглядит так:

|
| m `path termcap` "-поиск таблицы описания тер|
минала (termcap) и вывод
| . ее командой more
--More--(5%) "-строка состояния more
v vi /etc/termcap
vi +210 /etc/termcap "-командная строка для редак-
тора vi получена от more
:q "-выход из vi
--More--(5%) "-возврат в more
:!sh порождение нового shell'а
$ date запуск команды date
Wed Apr 23 07:15:04 PST 1986
$ ^d "-убрать порожденный shell
--More--(5%) "-возврат в more
:f распечатка имени файла,
выводимого командой more
"/etc/termcap" line 54 выход команды f
--More--(5%)
f "-команда more для пропуска
полного экрана
.skipping 23 lines
--More--(9%) "-пропуск и выдача текста
q выход из команды more
ПРИМЕРЫ
1. $ ll -R / | m
Начиная с корневого каталога (/), вывести в длинном формате (ll)
все файлы (опция -a подразумевается в ll) всей системы (-R) и постранично
распечатать на экран (| m).
2. $ m `path inittab rc passwd`
Обнаружить и вывести с помощью more системные файлы inittab, rc и
passwd. Неприятность здесь заключается в том, что данный маршрут скорее
всего относится к каталогу /bin/passwd, а не /etc/passwd (поскольку
каталог /etc размещается в конце каталогов), а это означает, что вы
можете попытаться вывести на экран исполняемый файл. В зависимости от
того, какую из версий команды more вы запустили, это может привести к
чему угодно начиная с сообщения команды more о том, что это был не
текстовый файл, и заканчивая тем, что ваш терминал начнет показывать
непонятные символы и даже зависнет.
ПОЯСНЕНИЯ
Поскольку в этом командном файле не так много текста, то все довольно
понятно, нет ни обработки ошибок, ни других дополнений. Просто
нехитрый вызов команды more. Полное имя здесь указано с целью повышения
быстродействия, как мы обсуждали ранее. Вы должны перепроверить
местонахождение вашей команды more. В системе Berkeley она может находиться
в каталоге /usr/ ucb/more. Воспользуйтесь командой path more
для определения этого места и вставьте соответствующий маршрут вместо
указанного нами.
Кстати, фокус попадания этой символьной строки в текст вашего командного
файла состоит в том, чтобы войти в редактор и вызвать следующую
команду:
:.!path more
Здесь происходит переход в shell и запуск команды path (:!), затем
выход команды path (который представляет собой полное маршрутное
имя) помещается в буфер редактора в самом начале текущей строки (.).
После этого вы имеете эти данные в вашем редактируемом файле и при необходимости
можете отредактировать их.
2.2.5. mmm - обработка программой nroff макрокоманд для рукописей

ИМЯ: mmm

mmm Командная строка nroff для макросов обработки
рукописей
НАЗНАЧЕНИЕ
Вызывает текстовый процессор nroff со специальными опциями, которые
инициализируют макросы обработки рукописей.
ФОРМАТ ВЫЗОВА
mmm file [...]
ПРИМЕР ВЫЗОВА
mmm memo Обработать с помощью nroff файл моих заметок
memo и отобразить его на экран
ТЕКСТ ПРОГРАММЫ
1 :
2 # @(#) mmm v1.0 Nroff command line with mm macros Author: Russ Sage
2а Командная строка nroff с макросами mm
4 if [ "$#" -eq 0 ]
5 then echo "mmm: wrong arg count" "&2
6 echo "usage: mmm file [...]" "&2
7 exit 1
8 fi
10 LIST=""
11 for ARG in $*
12 do
13 if [ ! -f $ARG ]
14 then echo "mmm: $ARG is not a regular file" "&2
15 else LIST="$LIST $ARG"
16 fi
17 done
19 nroff -r0O -mm $LIST
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ
ARG Содержит каждый позиционный параметр командной
строки
LIST Содержит список проверяемых имен файлов
ОПИСАНИЕ
ЗАЧЕМ НУЖЕН КОМАНДНЫЙ ФАЙЛ mmm?
Одним из фактов делового мира является работа с бумагами. Мы производим
заметки, письма, контракты, документы, руководства и так далее.
Если вы знакомы со стилем производства документации в системе
UNIX, то ваши текстовые файлы в основном представлены в одном из форматов
программы nroff.
Однако различные программы форматирования текстов служат различным
целям. Имеется стандартный nroff и nroffс дополнениями, такими
как макросы ms и mm. Для подготовки графической информации и выполнения
типографских работ разработана программа troff. Система AT&T имеет
целую программную среду под названием Writers Workbench, и система
Berkeley имеет аналогичные возможности.
Большинство наших задач по написанию каких-либо текстов может
быть сведено к нескольким стандартным форматам, таким как письма, рукописи
вообще, страницы руководств и так далее. Не так легко запомнить
опции команды nroff (или другой команды), которые следует использовать
в данном случае, да мы и не должны делать это. Наша команда mmm служит
иллюстрацией программы, которую мы можем запускать всякий раз, когда
нам нужен определенный формат. Вы можете создать несколько версий
программы, которые удовлетворяют вашим собственным нуждам при написании
текстов.
Использование заготовленных заранее команд означает, что мы можем
делать полезную работу, даже если некоторое время мы не выполняли работу
определенного вида. Мы также можем избежать многократных нажатий
на клавиши. Мастера UNIX'а периодически уединяются в своих горных убежищах,
где штудируют справочные руководства в поисках полезных, но
доселе незамеченных опций, которые могут быть встроены в программные
средства для повседневной работы. Если слишком некритично полагаться
на ваш текущий набор инструментальных средств, то можно пропустить полезные
возможности.
ЧТО ДЕЛАЕТ mmm?
Командный файл mmm - это интерфейсный процессор для команды
nroff. Под словом "интерфейсный" мы подразумеваем, что он обрабатывает
вызывающую командную строку и устанавливает все опции для вызова программы
nroff. Некоторые из опций nroff жестко запрограммированы в вызове.
Эти опции инициализируют отдельные части программы nroff.
Если вы не включаете никакие аргументы, mmm распознает это как
ошибку и выводит синтаксическую подсказку. Обратите внимание, что если
вы передадите mmm такой аргумент, как -z, то этот аргумент будет
рассматриваться как имя файла, а не как подлежащая передаче опция, и
это снова вызовет ошибку. Вторая ошибка не является фатальной, в то
время как первая фатальна.
После обработки всех аргументов программа nroff использует имена
файлов в качестве файлов с входными данными. По умолчанию вывод производится
в stdout (стандартный вывод). Обычно это экран вашего терминала,
но вывод может быть переадресован или передан по конвейеру на устройство
печати или куда-либо еще.
ПРИМЕРЫ
1. $ mmm nroffile | m
Запуск команды nroff применительно к файлу nroffile, вывод результата
на экран с передачей по конвейеру команде more. Это полезно
при изучении утилиты nroff, проведении экспериментов с различными командами
и наблюдения за соответствующими результатами.
2. $ for F in proj.?
do
mmm $F " $F.rf
done
Обработка в цикле всех файлов, имена которых содержат символьную
строку "proj.", за которой следует один символ. Это могут быть proj.1,
proj.2 и так далее по всему набору символов вплоть до proj.z, proj.{,
proj.|, proj.} и proj.~, если считать, что у вас есть файлы, имена которых
содержат эти символы. Каждый файл обрабатывается, и выход nroff
перенаправляется в файл с таким же именем, дополненным символами .rf.
3. $ mmm status[12] | lpr -o5
Обработка командой nroff файлов status1 и status2. Выход в стандартный
вывод передается по конвейеру программе lpr. Программа lpr является
фильтром и принимает или имена файлов в командной строке, или
непосредственно данные, передаваемые ей по конвейеру (но не то и другое
сразу). Опция -o5 указывает lpr сместить страницу на 5 символов.
ПОЯСНЕНИЯ
В строке 4 проверяется, равно ли нулю количество аргументов в командной
строке. Если да, в стандартный файл ошибок выдается сообщение
об ошибке. Выводится также синтаксическая подсказка, и mmm завершается
с плохим статусом.
Переменная LIST инициализируется нулевым значением в строке 10.
Обычно переменные интерпретатора shell и так в начале равны нулю, но
предварительная установка значения является хорошим стилем программирования.
Затем мы обрабатываем каждый аргумент командной строки в цикле
(строки 11-17). Все аргументы должны быть именами файлов, поэтому каждый
из них проверяется на то, существует ли он как обычный файл. Если
это не файл, то в стандартный файл ошибок выводится сообщение об ошибке.
Тем не менее программа не завершается. Не следует аварийно прекращать
всю программу только потому, что нет указанного файла. Мы пропускаем
его и идем до конца списка аргументов. Это особенно актуально,
если данная команда используется как фоновая во время выполнения другой
работы. Пользователь скорее согласится с тем, чтобы было выполнено
побольше работы, чем не сделано вообще ничего. Это решение, принятое в
данной программе, и вы можете изменить его, если оно не подходит в вашей
ситуации.
Если имени соответствует допустимый файл, оно добавляется в
список хороших имен файлов. Этот список становится главным списком для
команды nroff.
После того как все аргументы проверены, мы в строке 9 строим и
выполняем командную строку nroff.
Опция -rO0 для nroff указывает макросам обработки рукописей (пакету
mm) установить регистр, который имеет дело с отступом текста, в
состояние, соответствующее отступу в 0 символов. Это значит, что весь
текст начинается с крайней левой позиции, т.е. выровнен слева. Путем
проведения экспериментов я обнаружил, что левое выравнивание текста
программой nroff и установка отступа для принтера дает наиболее надежный
вывод на печать. В противном случае, если вы установите отступ
текста в nroff и отступ в принтере, то может произойти настоящее столкновение,
когда дело коснется вывода колонок в странице. Вы можете изменить
это место, если ваши программы вывода или устройства печати ведут
себя как-то иначе. Опция -mm указывает программе nroff просмотреть
библиотеку макросов обработки рукописей, чтобы определить, используются
ли какие-либо из них во входном документе. Эти макросы очень
большие и требуют много времени центрального процессора. Если вам необходимо
использовать их, то вам потребуется большой компьютер или
компьютер, специально предназначенный для этой цели, чтобы добиться
хорошего времени получения результата.
Последним аргументом является $LIST. В этой переменной находится
строка имен файлов, разделенных пробелами. Эти имена помещаются в командную
строку nroff. Можете быть уверенными, что в этом месте нет никаких
ошибок.
ВОЗМОЖНЫЕ МОДИФИКАЦИИ
Поскольку все аргументы рассматриваются как имена файлов, то у
нас нет способа передачи дополнительных команд пакету mm. Наличие такой
возможности было бы желательным, поскольку при экспериментировании
с командой nroff вам необходимо пробовать различные опции, чтобы увидеть,
как они действуют. Было бы тяжелой работой выполнять редактирование
текста mmm, чтобы добавить одноразовые опции, которые могут вам
никогда не понадобиться или опции, которые вы должны постоянно менять.
Один из путей достижения большей гибкости - посмотреть, имеет ли
какой-либо аргумент дефис в качестве первого символа. Если да, перехватить
эту опцию и убрать ее из списка имен файлов. После этого вы бы
имели список опций, которые нужно включить в командную строку, и
список имен файлов, подлежащих обработке.
Отметим, что место, занятое в нашем командном файле указанием пакета
mm, можно вместо этого заполнить ссылкой на другие макропакеты,
имеющиеся в вашей системе, например -ms или -me, в зависимости от нужного
вам формата. Отказ от поиска макросов, которые вам не нужны,
ускорит обработку: подробности вы найдете в документации по nroff или
troff.
2.2.6. pall - печать всех файлов в дереве

ИМЯ: pall

pall Распечатка всех файлов в дереве каталогов
НАЗНАЧЕНИЕ
Находит все файлы в заданном каталоге в соответствии с некоторым
критерием, разбивает файлы на страницы и помещает результат в один
файл, готовый к распечатке на принтере.
ФОРМАТ ВЫЗОВА
pall [-t|-d] directory
ПРИМЕР ВЫЗОВА
pall /usr/lib
Выводит на печать постранично все текстовые файлы в каталоге
/usr/lib
ТЕКСТ ПРОГРАММЫ
1. :
2 # @(#) pall v1.0 Print all files in a tree Author: Russ Sage
2а Печатает все файлы в дереве
4 if [ $# -eq 0 -o $# -gt 2 ]
5 then echo "pall: wrong argument count" "&2
6 echo "usage: pall [-t|-d] dir" "&2
7 echo " -t text (default)" "&2
8 echo " -d dev (.c,.h,.mk,.s)" "&2
9 exit 1
10 fi
12 NAME=""
13 if [ `echo $1 | cut -c1` = "-" ]
14 then case $1 in
15 -t) NAME=""
16 shift;;
17 -d) NAME="-name \"*.[chms]*\""
18 shift;;
19 *) echo "pall: invalid arg $1" "&2
20 echo "usage: pall [-t|-d] dir" "&2
21 echo " -t text (default)" "&2
22 echo " -d dev (.c,.h,.mk,.s)" "&2
23 exit 1;;
24 esac
25 fi
27 echo "creating output file: /tmp/lpr$$"
29 eval find $1 -type f $NAME -print | sort | while read FILE
30 do
31 if file $FILE |
32 egrep 'exec|data|empty|reloc|cannot open' "/dev/null 2"&1
33 then continue
34 else file $FILE " /dev/tty
35 pr $FILE
36 fi
37 done "" /tmp/lpr$$
39 echo "\nSend /tmp/lpr$$ to line printer (y/n): \c"
40 read CMD
41 if [ "$CMD" = "y" ]
42 then lpr /tmp/lpr$$
43 fi
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ
FILE Содержит имя каждого файла, который должен быть
обработан в цикле while
NAME Содержит строку поиска для определения местона-
хождения указанных файлов
CMD Содержит команду выдачи результатов на принтер
ОПИСАНИЕ
ЗАЧЕМ НУЖЕН КОМАНДНЫЙ ФАЙЛ pall?
Эта утилита объединяет концепции обхода дерева файлов и вывода
содержимого файлов. Даже когда файлы упрятаны в подкаталогах, мы все
равно хотим найти их. Нам необходима для этого утилита, которая обходит
древовидную структуру файлов, находит файлы нужного нам типа, готовит
их к распечатке и ставит в очередь для вывода на принтер.
Такого рода утилита особенно полезна, если исходные тексты или
файлы с документацией хранятся в иерархическом дереве. Дело осложняется
тем, что обычно эти текстовые файлы смешаны с исполняемыми файлами
(откомпилированными программами), файлами данных и, возможно, с архивными
файлами. Необходимо иметь возможность отфильтровать все непригодные
для печати файлы и подготовить текстовые файлы. Должны быть
проверены все файлы с тем, чтобы ни один не был пропущен.
Для выполнения всего этого процесса вручную требуется, чтобы вы
по команде cd переходили в каждый уровень дерева файлов, находили
текстовые файлы, обрабатывали их (обычно командой pr системы UNIX или
каким-либо другим текстовым процессором) и распечатывали их. После выполнения
всей этой работы вы еще должны собрать все отдельные распечатки
вместе в строго определенном порядке. Это большая работа, подверженная
ошибкам со стороны человека. Почему бы не позволить машине
выполнять эту работу? Сейчас мы имеем концепции и средства, необходимые
для создания такой утилиты.
В дополнение к возможности управления файлами, pall также может
управлять устройством печати. Обычно каждое задание, поставленное в
очередь на выполнение к принтеру, имеет заголовок, который печатается
первым. Это значит, что если вы поставили в очередь на печать десять
отдельных заданий, то впереди каждого из них будет две-три страницы,
которые должны быть убраны вручную. Помножьте это на сотни файлов, и
вы будете иметь кучу бумаг, которые должны быть выброшены.
Pall исключает эти потери, поскольку все обработанные данные собираются
в один большой текстовый файл. Когда вся обработка выполнена,
этот один файл может быть поставлен в очередь на печать или сохранен
для некоторых других целей. Единственное ограничение при таком подходе
заключается в максимальном размере файла, который вы можете создать.
Этот размер вычисляется умножением значения ulimit на размер блока.
Например, мое значение ulimit равно 4096. Размер блока в данном случае
равен 512, а не 1024. Максимальный размер файла равен 2097152. Вы можете
вычислить это прямо с клавиатуры, как показано ниже:
$ ulimit

4096

$ expr 4096 \* 512

2097152

Этого значения достаточно для большинства случаев.
ЧТО ДЕЛАЕТ pall?
Командный файл pall предназначен для поиска указанных файлов, обработки
их командой UNIX pr и сборки всех результатов в один файл.
После того как все исходные файлы будут обработаны командой pr, вам
будет задан вопрос о том, хотите ли вы поставить выводной файл в очередь
на печать к принтеру. Результирующий файл сохраняется в каталоге
/tmp, где его можно использовать для других целей или удалить.
Опциями pall являются -t и -d. Опция -t используется по умолчанию
и нет необходимости ее указывать. Она предназначена для документации и
указана в командной строке, чтобы более ясно показать действие, которое
будет выполнено.
Если выбрана текстовая опция, ищутся все файлы в древовидной
структуре и затем отбираются только текстовые файлы. Если указана опция
разработки -d (development), то ищутся только файлы, связанные с
разработкой программ. Затем эти файлы отфильтровываются с целью получения
текстовых файлов. Считается, что к разработке программ относятся
файлы, имена которых имеют вид *.c для исходных файлов на языке Си,
*.h для включаемых файлов заголовков, *.mk для файлов построения программ
(makefiles) и *.s для исходных файлов на ассемблере. Если требуются
какие-либо другие шаблоны, то такие символы могут быть легко помещены
в текст программы.
Прежде чем начнет выполняться поиск файлов, на экран выводится
имя временного файла, чтобы вы знали, как обратиться к нему после завершения
выполнения команды. Все результаты, полученные после обработки
файлов, направляются в этот один файл. Командный файл pall также
выводит на экран сообщения, когда он обрабатывает файлы. Вывод файлов
выполняется в реальном времени по мере обработки файлов. Распечатка
производится при помощи обычной команды UNIX'а file. По распечатке вы
можете судить о том, файлы какого типа обрабатываются в данный момент.
Если какой-либо норовистый файл проскользнет, то вы знаете, где он
размещен и какого он типа. Это делает отладку гораздо более простой.
Для файлов выполняется обработка, которая является действием по
умолчанию команды pr. Она разбивает файл на страницы и ставит заголовок
в начале каждой страницы. Заголовок содержит дату, имя файла и номер
страницы. Нет никакого иного способа передать заголовок командному
файлу pall во время его работы, поскольку он предполагает, что вы хотите
знать имя каждого файла таким, как оно есть на диске. Изменение
строки заголовка вызвало бы только неприятности и дополнительную работу.
Способ вызова pall влияет на формат имени файла в заголовке. Если
вы вызвали pall, используя абсолютное имя каталога, то в распечатке
используются полные маршрутные имена. Если вы вызвали pall с относительными
маршрутными именами, то они и используются при выводе на печать.
Внутри pall используется команда find системы UNIX. Команда find
использует данные из командной строки, т.е. те, которые ввел пользователь.
Выводимый заголовок изменяется в зависимости от того, что указано
в командной строке, которую использует find. Если вы вызываете
pall, используя следующую командную строку, то заголовок содержит полное
маршрутное имя:

|
| $ pall /usr/include
|
| May 5 10:39 1986 /usr/include/a.out.h Page 1
| .
| .
| .
| May 5 10:39 1986 /usr/include/ar.h Page 1
| .
| .
| .
Если вы вызываете pall с помощью относительной нотации, то имена
файлов также являются относительными, что не очень хорошо. Если у вас
есть несколько каталогов, в которых имеются одинаковые имена файлов,
то вы не сможете быть уверены, что смотрите на правильную распечатку.
Вот как это может выглядеть:

|
| $ cd /usr/include
| $ pall .
|
| May 5 10:39 1986 ./a.out.h Page 1
| .
| .
| .
May 5 10:39 1986 ./ar.h Page 1
ПРИМЕРЫ
1. $ pall /usr/include
Выводит ВСЕ файлы заголовков. Сюда включаются файлы заголовков в
подкаталоге sys и во всех других каталогах, которые могут располагаться
ниже каталога /usr/include. Это и есть причина, по которой был
написан командный файл pall. Он создает один огромный листинг всех
файлов заголовков в отсортированном порядке с печатью в заголовке
страниц полного имени.
2. $ pall $HOME/src
Обходит все каталоги, находящиеся ниже каталога исходных текстов,
и распечатывает все файлы.
ПОЯСНЕНИЯ
В самом начале производится проверка на наличие ошибок в командной
строке. Если в ней нет аргументов или их больше двух, выводится
сообщение об ошибке, синтаксическая подсказка и программа завершается
с неудачным статусом возврата.
В строке 12 инициализируется переменная NAME. Это действие выполняется
по умолчанию, поэтому данная строка дает возможность не указывать
опцию в командной строке. Оператор if в строке 13 означает: "Если
первым символом первого аргумента является дефис", то нужно проверить,
какая это опция.
Если установлена опция -t, то переменная NAME инициализируется
нулевым значением, что совпадает с действием по умолчанию, поэтому на
самом деле ничего не меняется. Затем эта опция удаляется из командной
строки.
Если установлена опция -d, то переменная NAME инициализируется
для поиска символьной строки, соответствующей именам файлов программной
разработки. Обратите внимание, что двойные кавычки внутри оператора
экранированы, т.е. впереди них стоят символы наклонной черты. Командный
интерпретатор shell воспринимает кавычки вокруг строки поиска
на первой фазе синтаксического разбора без отмены присвоения, тем самым
оставляя двойные кавычки последующей команде find.
Если опцией является что-либо другое, выводится сообщение об
ошибке и программа завершается.
В строке 27 выводится сообщение о том, какой файл содержит результаты
работы. Сам этот оператор не создает файл. Он только печатает
имя файла, который будет создан позже.
Строки 29-43 - это главный цикл всей программы. Оператор find
должен быть повторно проанализирован командой eval, поскольку переменная
NAME содержит нужные нам данные. Если бы не было команды eval, то
подстановка символьных строк выполнялась бы неправильно. Обратите внимание,
что переменной NAME не требуются кавычки в строке 24. Они уже
есть в переменной NAME, так как были обработаны командой eval.
Оператор find находит только файлы типа f, или обычные файлы,
т.е. не каталоги и не символьные или блочные устройства. Здесь под
"обычными файлами" понимается, что они еще могут быть файлами данных
или исполняемыми. Если значение переменной NAME равно нулю, оно не
влияет на командную строку. Если NAME содержит символы файлов программной
разработки, то они становятся частью команды find при выполнении
команды eval. Это накладывает ограничения на шаблон поиска команды
find. Когда файлы найдены, их имена выводятся в стандартный вывод.
Стандартный вывод по конвейеру передается команде sort, располагающей
имена файлов по порядку. Это сильно помогает при сортировке горы выводной
информации. Чтение кипы распечаток толщиной в один фут может
свести с ума кого угодно.
Отсортированные имена по конвейеру передаются в цикл while, который
читает имена файлов по одному. Обратите внимание, что стандартный
вывод для всего цикла while переадресовывается во временный файл, что
облегчает сборку всех выходных результатов в одном месте вместо переадресации
каждого вызова команды в файл.
Для каждого подходящего файла выполняется проверка в строках
31-36. Проверка начинается с запуска команды file. Выход file по конвейеру
передается команде egrep, которая ищет тип файла, соответствующий
набору нескольких выражений. Если какое-либо выражение подходит,
то нам не нужно обрабатывать этот файл. Это не текстовый файл, и его
нельзя вывести на принтер. Во многих случаях файлы данных содержат
большое количество символов прогона формата, которые выталкивают страницу
после каждой пары символов. Если вы не будете находиться рядом с
принтером, когда печатаются такие файлы, то вы можете получить
листинг, занимающий половину ящика бумаги, затратив целый лес на ненужную
работу.
Нам не нужен выход команды egrep, а только ее статус возврата.
Если egrep обнаруживает одно из указанных ей выражений, она завершается
со статусом успеха, или 0. Тем самым проверка if включает выполнение
оператора then, который в данном случае выводит нас из конструкции
if-then-else и продолжает цикл while, пропуская таким образом
файл.
Если же egrep не обнаружила ни одну из указанных символьных
строк, то выполнение продолжается с оператора else, который выполняет
еще одну команду file и переадресовывает ее вывод на устройство с именем
/dev/tty. Это универсальное имя устройства, которое гарантирует
вам вывод на экран вашего терминала. UNIX обеспечивает, что указание
/dev/tty обходит любые команды переадресации вывода, действующие в
данный момент. Поскольку стандартный вывод уже переадресован для всего
цикла while, то нам нужно попасть на устройство /dev/tty, чтобы вывод
шел на экран терминала, а не в файл печати. Отображение на терминал
имени обрабатываемого файла позволяет пользователю знать, какой файл
будет добавлен к распечатке.
Если файл удовлетворяет нашим критериям, он обрабатывается командой
pr. Результат направляется в стандартный вывод, который переадресован
циклом while так, чтобы результат четко попадал в один файл.
Отметим, что нам нужно поставить символ добавления в файл вывода ("").
В противном случае мы получим запись на место существующего файла печати,
а значит в файле печати будет находиться только последний обработанный
файл.
После того как все файлы обработаны, задается вопрос о том, хотите
ли вы вывести результирующий файл на печать. Предлагается ответить
на этот вопрос "да" (yes) или "нет" (no), однако в программе проверяется
только положительный ответ (yes). Это значит, что нажатие любой
клавиши трактуется как ответ "no", кроме клавиши "y", означающей "да".
Ответ пользователя читается с клавиатуры, и проверяется, является ли
он символом "y". Если да, то файл ставится в очередь на печать. Если
нет, дальнейшая проверка не производится и командный файл завершается.
Отметим, что запрос о выводе на печать поступил в стандартный вывод.
Переадресация вывода действовала только во время работы цикла
while, а затем прекратилась. Так было сделано потому, что цикл while
был фактически еще одним порожденным shell-процессом (subshell) и переадресация
действовала только в этом те внимание, что маршрутнаяустановите
значения переменных вне цикла, а затем измените их внутри
цикла. После завершения цикла переменные по-прежнему имеют свои первоначальные
значения, а не измененные в цикле значения. Измененные значения
касались переменных порожденного интерпретатора shell, которые
исчезли, когда порожденный shell завершился. Переменные интерпретатора
shell могут передавать значения только вниз, порожденным процессам, но
процессы-потомки не могут передавать значения переменных вверх, родительскому
процессу. Обычно передача значений переменных поддерживается
при помощи какого-либо файла, в котором хранятся данные для обмена
между родительским процессом и процессом-потомком.
УПРАВЛЕНИЕ ВЫВОДНЫМИ ФАЙЛАМИ БОЛЬШИХ РАЗМЕРОВ
Как мы уже отмечали, общий размер выводного файла ограничен. Напомним,
что команда find проходит все дерево каталогов вниз до конца
по всем поддеревьям, начиная с каталога, имя которого указано в командной
строке. Если вы находитесь на вершине очень глубокого дерева,
то обрабатываться могут буквально сотни файлов. Поскольку вы ограничены
максимальным размером выводного файла, вы можете обработать только
ограниченное число файлов. Конечно, количество файлов, которое вы можете
обработать, зависит также от того, насколько велики входные файлы.
Если выводной файл достигает своего максимума, все добавляемые
после этого данные теряются. Потеря данных весьма болезненна, и обычно
требуется некоторое время, чтобы ее обнаружить. В медленно работающей
системе попытка обработать большое дерево, например все исходные
тексты системы UNIX, может занять целый час и даже больше, прежде чем
выходной файл заполнится. Это означает, что вы должны находиться рядом
и следить за тем, когда файл переполнится. Если он все-таки переполнился,
вы должны все выбросить и начать сначала. Это также означает,
что вы должны перейти вниз по дереву. Это может быть проблемой в сбалансированных
деревьях.
Например, рассмотрим каталог /usr/lib. Этот каталог содержит много
файлов на первом уровне и много каталогов первого уровня. Если бы
мы не обработали все файлы каталога /usr/lib за одну попытку, мы должны
были бы пойти вниз по подкаталогам каталога /usr/lib. Попытки делать
это вручную и запускать pall в каждом подкаталоге заняли бы много
времени и могли бы привести к ошибкам с вашей стороны. Кроме того,
pall допускает указание только одного имени каталога, что приведет к
получению большого количества распечаток и к путанице при их сортировке.
Что же делать? Радикальным решением является увеличение значения
ulimit. Вы можете сделать это либо с помощью программы на языке Си,
использующей системный вызов ulimit, либо командой shell'а ulimit.
Техника выполнения такой работы представлена в главе 7.
ВОЗМОЖНЫЕ МОДИФИКАЦИИ
Возможно, вы захотите добавить свои собственные штрихи в некоторых
местах командного файла. Первым местом является то, где указываются
символы поиска файлов программной разработки. Символы, использованные
нами - это наиболее употребимые суффиксы в UNIX. Если вы
используете не Си и ассемблер, а другие языки, то вы можете добавить
соответствующие символы.
Следующим местом, где могут быть сделаны дополнения, являются опции,
которые может понимать pall. Вам могут понадобиться файлы с определенными
именами или определенными типами, например, файлы nroff. Эти
опции могут быть легко добавлены в оператор case, что улучшит команду.
Последним местом возможных изменений является тип файлов, которые
нужно пропускать. Символьная строка для команды egrep покрывает большинство
важных нетекстовых типов файлов. В вашей системе могут быть
какие-то особые типы или же имена могут быть другими. Если вам необходимо
дополнить строку, сделайте это. Команда egrep может обработать
довольно много информации. Я не знаю ее ограничений. Возможно, вы обнаружите
их, просматривая исходный текст утилиты egrep. Если получается
слишком длинная строка и не помещается на экране, ничего страшного.
Перенос на следующие строки экрана не опасен, пока общее количество
символов не превысит 255. Опасно только указывать переадресацию
символьной строки if на нулевое устройство в следующей после команды
egrep строке. Кажется, что все работает правильно, но это не так. Переадресация
должна указываться в той же строке, где стоит команда
egrep.
В данной главе сделано очень много. Наиболее важно то, что получено
множество новых идей, которые можно использовать при эксплуатации
программной среды и просмотре файлов любого типа. В следующей главе мы
углубимся в рутинную работу по ежедневному сопровождению файлов и
используем изученные средства, чтобы они облегчили нашу жизнь.
* ГЛАВА 3. Поддержка файловой системы * ВВЕДЕНИЕ
3.1. СОПРОВОЖДЕНИЕ ФАЙЛОВ
3.1.1. Операции сопровождения
3.1.2. Средства пересылки файлов
3.1.3. Средства копирования
3.1.4. Средства проверки операции копирования
3.2. ПЕРЕСЫЛКА ФАЙЛОВ
3.2.1. cptdir - копирование дерева каталога
3.2.2. can - удаление файлов в "мусорную корзину"
3.2.3. dosflp - копирование файлов с гибкого диска формата MS-DOS
с использованием символов шаблона в именах файлов
3.3. СРЕДСТВА ПОЛУЧЕНИЯ РЕЗЕРВНЫХ КОПИЙ
3.3.1. autobkp - автоматически наращивамый файл резервной копии
3.3.2. cpiobr - копирование и восстановление файлов в виде потока
данных
3.4. СРЕДСТВА ПРОВЕРКИ ОПЕРАЦИЙ КОПИРОВАНИЯ
3.4.1. dsum - контрольные суммы двух катологов
3.4.2. log - меню доступа к файлам протокола копирования
ВВЕДЕНИЕ
Даже "небольшая" система UNIX с малым числом пользователей порож-
дает сотни файлов в ходе обычной работы. В процессе программирования вы
можете создавать множество файлов для различных версий ваших программ.
Ведение почты и запись текста при помощи редактора vi способствует то-
му, что накапливается еще больше файлов. Такие утилиты, как uucp, lp и
другие добавляют еще больше файлов. Если у вас система UNIX установлена
на микро-ЭВМ, то ваш жесткий диск начинает переполняться. В больших
многопользовательских системах дисковая память редко считается пробле-
мой, но в действительности всегда кажется, будто файлы стремятся расши-
риться до заполнения всей доступной дисковой памяти. Поэтому каждый
пользователь должен нести ответственность за расход дискового прост-
ранства. (Если вы платите за дисковую память, то у вас также могут быть
финансовые стимулы.) Однако, то, что вы хотите сохранить, вы хотите
СОХРАНИТЬ. Именно здесь начинается работа по созданию резервных копий.
3.1. СОПРОВОЖДЕНИЕ ФАЙЛОВ
В предыдущей главе мы разработали некоторые средства поиска и
отображения информации, помогающие нам поддерживать жизненный путь всех
наших файлов. Теперь мы собираемся обратиться к важнейшим рутинным ра-
ботам, которые позволят избежать хаоса и катастрофы.
Сопровождение файлов означает избавление от файлов, которые нам
больше не нужны и в то же время систематическое копирование тех файлов,
которые мы хотим сохранить. Для этого требуется возможность использова-
ния разнородных доступных носителей данных. Сопровождение файлов подра-
зумевает также ряд систематических, повторяющихся задач, а это означа-
ет, что мы можем создать средства системы UNIX для автоматизации этого
процесса.
3.1.1. ОПЕРАЦИИ СОПРОВОЖДЕНИЯ
Сопровождение файлов включает два вида операций: создание резерв-
ных копий (копирование) и удаление "мусора".
Копирование - это дань уважения, которую мы платим за хрупкость
физических данных в руки Мерфи и других богов энтропии. Хорошее
средство копирования является быстрым, гибким, простым в использовании
и стимулирует пользователей часто копировать самые важные файлы. В
последующем тексте будут представлены различные методы копирования,
пригодные для разных конфигураций системы и типов носителей.
Имеется два вида резервных копий: "мягкие" и "твердые". "Мягкие"
резервные копии - это копии в другом файле или каталоге в той же или в
другой файловой системе (т.е. разделе) на том же или другом жестком
диске. Такого рода копирование сделать легко и оно предохраняет от на-
носимого самому себе ущерба, такого как удаление файла по невниматель-
ности. Чаще всего для такого типа копирования используется наше
средство cptdir. Основной недостаток мягкого копирования заключается в
том, что вы по-прежнему уязвимы для таких воздействий, которые влияют
на ваш физический носитель (обычно жесткий диск) так, что и оригинал и
копия оказываются разрушенными.
"Твердая" копия - это копия на другом устройстве или даже в другой
системе UNIX. Средства, представленные ниже в данной главе, управляют
такого рода копированием и дают вам возможность выполнять копирование
такого типа и с такой периодичностью, которые соответствуют объему ва-
шей вычислительной системы, уровню ее активности и важности хранимых
данных.
Твердое копирование всегда несколько утомительно, потому что диски
или ленты должны быть смонтированы (или должна быть установлена связь с
другой системой), а эта операция требует много времени. Преимущество,
естественно, заключается в том, что вы больше не зависите от целост-
ности какого-либо одного устройства.
Автоматизируя нашу процедуру копирования, мы стараемся сделать его
как можно менее болезненным. Делая наши средства копирования в какой-то
степени разумными, мы можем выбрать только файлы, которые нуждаются в
копировании, и тем самым сохранить время и память. Наилучший способ
обеспечить, чтобы копирование выполнялось регулярно - минимизировать
время и требуемые для этого усилия. Наконец, создание процедур для про-
верки правильности копий даст вам спокойствие духа.
"Удаление мусора" можно автоматизировать путем указания и подго-
товки к удалению файлов, которые, вероятно, будут временными, либо ка-
ких-то других файлов, которые созданы (но не обязательно разрушены) при
компиляции, выполнении конвейеров или другими операциями. Вы также мо-
жете указывать файлы, специфичные для ваших работ как не подлежащие
удалению.
3.1.2. СРЕДСТВА ПЕРЕСЫЛКИ ФАЙЛОВ
Первая группа средств - это простые универсальные переносчики фай-
лов. Программа cptdir может копировать каталог (и любые подчиненные ка-
талоги, лежащие ниже в дереве) в каталог-приемник. Каталог-приемник -
это обычно каталог, назначенный в качестве резервной копии для некото-
рого проекта.
Программа can берет на себя необходимую рутинную работу - убирает
"мусор". Эта программа позволяет вам выбрать типы временных файлов, ко-
торые должны периодически удаляться. Направляя их в "мусорный" каталог,
can предоставляет вам возможность просмотреть все, что было удалено, и
восстановить то, что вы на самом деле хотите сохранить.
Программа dosflp допускает применение символов-шаблонов в именах
файлов, используемых при копировании отобранных файлов с дискет формата
MS-DOS в XENIX. Это упрощает операцию копирования и уменьшает число на-
жатий на клавиши.
3.1.3. СРЕДСТВА КОПИРОВАНИЯ
Далее представляется "рабочая лошадка" - средства копирования.
Autobkp использует список маршрутных имен, чтобы определить, какие
части файловой системы должны быть проверены. Затем эта программа копи-
рует из выбранных областей те файлы, которые были добавлены или измене-
ны в последние 24 часа.
Cpiobr предоставляет интерактивное дополнение к команде cpio
системы UNIX. Она позволяет вам скопировать файлы с жесткого диска на
гибкий и, если необходимо, восстановить их с гибкого диска на жесткий.
3.1.4. СРЕДСТВА ПРОВЕРКИ ОПЕРАЦИЙ КОПИРОВАНИЯ
Выполнение копирования не избавит вас от волнений, пока вы не бу-
дете знать, что вы скопировали все, что хотели, и что копирование прош-
ло корректно. Программа dsum использует контрольную сумму для проверки
того, что исходный каталог и каталог-копия содержат одни и те же файлы.
Программа log отображает регистрационный файл, чтобы показать, какое
автоматическое копирование выполнялось в четыре часа утра, когда вы
(надеемся) спали.
При создании этих средств мы просмотрим некоторые важные команды
системы UNIX и обнаружим новые способы их использования. Когда вы про-
работаете всю данную главу, вы будете знать, как работать "с мельчайши-
ми частицами" при использовании файловой системы UNIX. Вы сможете авто-
матизировать иную большую область ваших компьютерных будней. Вы должны
суметь создать пользовательские утилиты копирования и верификации,
удовлетворяющие вашим нуждам. Вы сможете перевести вашу систему в режим
работы, обеспечивающий ей самостоятельное выживание. (Мы оставляем фи-
лософам определять, даст ли это вашему компьютеру примитивную форму
жизни!)
Между прочим, многие средства в данной главе пользуются преиму-
ществами рекурсивных методов обхода дерева, которые мы разработали в
предыдущей главе. Вы можете просмотреть тот материал, если у вас име-
ются затруднения в понимании того, что представлено здесь.
3.2. ПЕРЕСЫЛКА ФАЙЛОВ
3.2.1. cptdir - копирование дерева каталога
ИМЯ: cptdir
cptdir Копирует дерево каталога в другое место
ФУНКЦИЯ
Копирует дерево файловой системы, корень которого расположен в ка-
талоге, в другой каталог системы. Нет ограничений на какой-либо специ-
фический каталог или жесткий диск.
ФОРМАТ
cptdir [-s] каталог-источник каталог-приемник
ПРИМЕР ВЫЗОВА
cptdir $HOME /bkp
Копирует каждый файл из $HOME в каталог /bkp.
КОМАНДНЫЙ ФАЙЛ cptdir
1 :
2 # &(#) cptdir v.1.0 Copy a directory tree Autor: Russ Sage
4 if [ $# -lt 2 -o $# -gt 3 ]
5 then echo "cptdir: argument error" "&2
6 echo "usage: cptdir [-s] srcdir desdir" "&2
7 echo " -s silent mode" "&2
8 exit 1
9 fi
11 if [ "$1" ="-s" ]
12 then OPT="-pd"
13 shift
14 else OPT="-pdv"
15 fi
17 SRC=$1
18 DEST=$2
19 umask 0
21 if [ -d $DEST ]
22 then echo "\"$DEST\" already exist. Remove it? (y/n): \c"
23 read CMD
24 if [ "$CMD" = "y" ]
25 then rm -rf $DEST
26 mkdir $DEST
27 fi
28 else mkdir $DEST
29 fi
31 if [ "`echo $DEST|cut -c1`" = "/" ]
32 then cd $SRC
33 find . -print | sort | cpio $OPT $DEST
34 else PWD=`pwd`
35 cd $SRC
36 find . -print | sort | cpio $OPT $PWD/$DEST
37 fi
ПЕРЕМЕННЫЕ СРЕДЫ
CMD Команда, полученная от пользователя
DEST Каталог-приемник, в который нужно копировать
OPT Опции, которые передаются утилите cpio
PWD Текущий рабочий каталог
SRC Каталог-источник, из которого нужно копировать
Описание
Зачем нам нужен cptdir?
Мы уже отмечали необходимость в дополнительных командах, которые
рекурсивно обходят древовидную структуру файловой системы UNIX. В ран-
них версиях UNIX единственная команда tar могла управлять движением по
дереву. В более новых версиях системы имеется опция -r в команде cp,
которая делает cp рекурсивной (эта возможность реализована только в
последней версии System V) и команда cpio. Последняя является многоце-
левой командой копирования, которая может иметь дело как с потоковым
форматом, так и с форматом файловой системы.
Проблема при использовании даже таких улучшенных стандартных ко-
манд системы UNIX состоит в том, что вам необходимо указать множество
деталей и убедиться в том, что вы правильно используете синтаксис.
Ошибки могут привести к потере времени и даже хуже того, к неожиданным
побочным эффектам. С некоторыми из этих эффектов связаны изменения прав
доступа и владельца, порядок распределения индексных дескрипторов фай-
лов (inode), размещения файлов-приемников и результирующие полные име-
на. Очень много необходимо запомнить и заново вызывать каждый раз при
копировании. Поскольку такое копирование делается не часто, тяжело за-
помнить все эти детали. Мы разрешаем эту проблему, автоматизируя детали
процесса и в то же время предоставляя пользователю гибкость и управле-
ние результатами. Мы создаем инструменты для управления файлами, кото-
рые являются хорошими дополнительными средствами к основным командам
системы UNIX.
Что делает cptdir?
Процедура cptdir копирует каталог (и все дерево под ним, если оно
существует) в другой каталог системы. Поскольку каталоги предусматрива-
ют логический доступ и не являются аппаратно-зависимыми (в отличие от
имен устройств), то вы можете легко копировать файлы в другое место на
том же диске или копировать их на другой диск полностью без специально-
го синтаксиса или опций.
Вы можете указать, хотите ли вы, чтобы на экран выводились имена
копируемых файлов. Если вы не хотите этого, используйте опцию -s
("silent" - молчаливый). По умолчанию используется режим "verbose"
(многословный), который отображает имена по мере копирования файлов.
Заметьте, что это копирование, а не перемещение файлов. Недостаток
копирования в отличие от перемещения заключается в том, что если прием-
ником является каталог на том же диске, то вам требуется дополнительное
место на диске для размещения второго образа. Вам также необходимо
иметь достаточно описателей файлов (inodes) для сохранения всех файлов.
В противном случае вы можете лишиться шанса сбросить в "мусорную корзи-
ну" ваши рабочие файлы.
В командной строке допустимо указание каталога-источника и имя ка-
талога-приемника. Единственный ключ, допустимый в командной строке -
это "-s". Любой другой ключ приводит к завершению команды, не вызывая
никаких разрушений. Вы, конечно, можете добавить программный код с
целью проверки опции и выдачи сообщения о допустимых ключах, если ука-
зано нечто отличное от -s. Если вы делаете еще какую-либо проверку на
наличие ошибок сверх того, что требуется для предотвращения разрушения
данных или системы, то это дело личного вкуса. Минимизация проверок на
наличие ошибок дает более компактные и быстрые сценарии, подходящие для
опытных пользователей.
Если указанный каталог-приемник не существует, то он создается.
Если каталог-приемник уже существует, выдается сообщение об этом и вам
задается вопрос о том, хотите ли вы очистить его. Если вы ответите
"yes", каталог уничтожается и создается снова пустым. Если вы ответите
"no", каталог остается таким, какой есть и копируемые файлы просто до-
бавляются к уже существующим в наличии. При этом может возникнуть неко-
торая путаница, особенно если некоторые файлы с такими именами уже су-
ществуют в каталоге-приемнике. В большинстве случаев, однако, у пользо-
вателей не появляется желания добавлять свою копию в существующий ката-
лог.
Тем не менее каталог-приемник должен быть создан, поскольку необ-
ходимо его наличие, чтобы команда cpio работала правильно. Если же его
нет, cpio не выполнится и выдаст сообщение об ошибке.
Процедура cptdir начинает копирование путем прохождения по катало-
гу-источнику и формирования списка файлов, находящихся в нем, рекурсив-
но обходя дерево сверху вниз. В результате может получиться, что скопи-
руется больше, чем вы планировали, поэтому вам необходимо знать размер
файловой структуры, которую вы хотите скопировать. Затем файлы копиру-
ются в каталог-приемник. Исходные файлы никак не модифицируются и не
изменяются (за исключением того, что дата последнего доступа может быть
модифицирована).
Когда идет копирование, на экран выдается сообщение от cpio, кото-
рое показывает полный маршрут к файлам-приемникам. Этот маршрут должен
соответствовать маршруту, указанному в командной строке, в противном
случае что-то не так.
Примеры
1. $ cd /mnt
$ cptdir /bin .
Перейти на другой диск (обычно смонтированный в каталоге /mnt) и
копировать все файлы из каталога /bin в текущий каталог. Обратите вни-
мание, что результирующими файлами будут /mnt/*, что может не совпадать
с вашим желанием.
2. $ cd /bin
$ cptdir . /mnt/bin
То же, что и в предыдущей команде, но обратите внимание, что точка
изменила свою позицию. Команда указывает копирование всех файлов теку-
щего каталога в каталог /mnt/bin. Получаются файлы /mnt/bin/*, что выг-
лядит более резонным.
3. $ cptdir /bin /mnt
То же, что и в примере 1.
4. $ cptdir /bin /mnt/bin
То же, что и в примере 2.
Пояснения
В строках 4-9 производится проверка аргументов командной строки.
Если указано меньше двух аргументов, этого недостаточно. Как минимум
должны быть указаны имена каталога-источника и каталога-приемника. Бо-
лее трех аргументов слишком много. Самое большее, там должны быть опция
-s, каталог-источник и каталог-приемник.
В строках 11-15 устанавливаются ключи команды cpio. По умолчанию
это pdv, что означает "pass" (передача) для копирования в формате фай-
ловой системы (в отличие от необработанного потока данных), "directory"
(каталог) для создания каталога при необходимости и "verbose" (мно-
гословный) для выдачи имен файлов по мере их копирования. Если первым
позиционным параметром является ключ -s, который указывает запуск
cptdir в молчаливом режиме, ключи команды cpio не содержат ключа выдачи
сообщений и, таким образом имена файлов не выдаются на экран.
Строки 17,18 и 19 устанавливают каталоги "откуда" и "куда" и уста-
навливают переменную umask в 0. Переменная umask определяет подразуме-
ваемые права доступа для всех файлов, созданных нашим командным про-
цессором. Мы изменяем umask для гарантии того, что все файлы копируются
в дерево-приемник и ни один из них не будет заблокирован из-за
отсутствия прав чтения или записи. Побочным эффектом является то, что
все каталоги имеют права доступа вида rwxrwxrwx, а все файлы - вида
rw-rw-rw-, что может потребовать изменений для обеспечения вашей безо-
пасности. Изменение umask имеет действие только на время работы проце-
дуры. Когда cptdir завершается, umask вашего вызывающего командного
процессора остается неизменным.
Строки 21-29 выполняют проверку каталога-приемника. Если он уже
существует, вас запрашивают, нужно ли его удалить и заново создать.
Если он не существует, он создается для работы cpio.
Строки 31-36 выполняют непосредственно копирование. Прежде чем
объяснить, что здесь делается, давайте сперва посмотрим, как работает
cpio. Поскольку оператор find генерирует список файлов, нам необходимо
представлять, как его выход может влиять на выполнение cpio.
Если мы указали "find . -print", то полные имена файлов будут
иметь точку впереди, например:
./dir
./dir/file1
./dir/file2
Это относительная нотация, которая очень полезна, когда вы не хо-
тите, чтобы ваши файлы передавались согласно абсолютным маршрутным име-
нам, но хотим сохранить их взаимосвязь друг с другом. Если на них ссы-
латься относительно точки, то место, куда они будут помещены, может
быть, где угодно. Однако, если мы скажем "find /dir -print", список бу-
дет выглядеть так:
/dir
/dir/file1
/dir/file2
В обоих случаях мы ссылаемся на наш текущий каталог, но применение
записи вида /dir заставляет полное имя начинаться с "/" и не допускает
использование относительной нотации. Передача такой же информации ко-
манде cpio может радикально изменить место размещения ваших файлов.
Например, если я сказал "cd /src; find . -print | cpio -pdv /dest", ре-
зультирующий список будет таким:
/dest/./dir
/dest/./dir/file1
/dest/./dir/file2
где на первом месте стоит, вероятно, то, что вы хотели. Однако,
если я сказал "find /src -print | cpio -pdv /dest", результирующие
маршрутные имена будут такими:
/dest/src/dir
/dest/src/dir/file1
/dest/src/dir/file2
что не очень хорошо, поскольку это создает уровень каталога, в ко-
тором нет необходимости. Заметьте, что имя каталога "src" было перехва-
чено при распечатке. Это произошло потому, что его выдал find, а cpio
считает, что src было частью имени каталога-приемника.
Повсеместное использование относительной нотации может привести
нас к потере уже имеющейся информации. Например, если бы я сказал "cd
/nowhere; find /src ....", каталог-приемник получил бы неверное имя. Мы
должны уметь использовать этот тип нотации и не попадать в ловушки син-
таксиса. Это и есть то, что делает cptdir.
В строке 31 производится проверка на то, является ли первый символ
в маршрутном имени целевого каталога символом "косая черта" ("/"). Если
да, то мы точно знаем, что имя каталога-приемника выражено в виде абсо-
лютного маршрутного имени, поэтому мы можем сменить каталоги без потери
информации о нашем текущем каталоге. В строках 32-33 мы переходим в ка-
талог-источник и копируем файлы.
Но если первый символ каталога-приемника НЕ является наклонной
чертой, используемая нотация является относительной. Это значит, что
если мы сменим каталог, мы потеряем информацию о том, где мы находи-
лись, когда был запущен командный файл. Чтобы избежать этого, мы полу-
чаем в строке 34 полное имя текущего каталога путем перехвата вывода
команды pwd и присвоения этого значения переменной таким образом, что
позже мы сможем ее восстановить. Затем мы переходим в каталог-источник
и копируем файлы, используя префикс абсолютного маршрутного имени от
команды pwd и относительный суффикс того места, где мы находимся.
Причиной того, что мы так поступаем, является использование от-
носительной (точечной) нотации в операторе find. Как можно было видеть
в предыдущем описании, отказ от использования точечной нотации может
привести к путанице в маршрутных именах каталога-приемника. Для того
чтобы всегда использовать точку в операторе find, нам необходимо убе-
диться, куда мы собираемся пересылать файлы. Еще раз напомним, что ко-
манда cd действует только для данного командного процессора "низкого"
уровня, поэтому она не влияет на тот командный процессор, который за-
пустил командный файл. Вообще, командный файл должен оставить пользова-
телей в тех же условиях, в которых они находились перед его запуском,
за исключением выполнения необходимых работ, при которых не произво-
дится смена текущего каталога.
Когда cptdir завершается, управление возвращается вызывающему ко-
мандному процессору, который по-прежнему ведет свой собственный текущий
каталог. Напомним, что всегда, когда вы переходите на более низкий уро-
вень командного процессора, экспортируемые переменные передаются вниз,
но НИЧЕГО не передается наверх.
ВОЗМОЖНЫЕ МОДИФИКАЦИИ КОМАНДНОГО ФАЙЛА
В нынешней реализации никакие дополнительные ключи не допускается
передача никаких дополнительных ключей команде cpio. Что случится, если
вы захотели заменить копирование файлов, где это возможно, созданием
ссылок (ключ -l) или не менять время последнего доступа к исходному
файлу при его копировании (опция -a)? Такие возможности были бы недо-
пустимы.
Можно легко добавить возможность передачи дополнительных аргумен-
тов. Они должны быть опознаны как аргументы, сохранены и затем выбраны
из командной строки.
Для этого потребуется такой цикл:
for ARG in $*
do
if [ "`echo $ARG|cut -c1`" = "-" ]
then CPIOARG="CPIOARG $ARG"
shift
fi
done
Затем переменная CPIOARG может быть передана команде cpio.
Еще одна область, где могут быть произведены изменения - это уп-
равление правами доступа к файлам. Как объяснялось ранее, значение 0
для umask делает все права такими, что они разрешают запись. Если это
вам не подходит, оператор find может быть изменен так, что будет произ-
водиться выборочное копирование (и изменение прав доступа).
Предположим, например, вы имеете каталог с двумя файлами. Если вы-
полнился оператор "find /dir -print", список файлов будет таким:
/dir
/dir/file1
/dir/file2
Обратите внимание, что имя каталога появляется первым. Проблема
возникает, если имя каталога не принадлежит вам или вы не имеете права
записи. Происходит следующее: имя каталога копируется первым, устанав-
ливаются права доступа (блокируя вас) и после этого file1 и file2 не
могут быть скопированы в каталог dir. В cptdir мы применяем решение из-
менить umask так, чтобы вы всегда имели права записи. Это своего рода
клудж, но он работает.
Другой путь - это изменить оператор find. Выполнение оператора
"find /dir -depth -print" сгенерирует такой список файлов:
/dir/file1
/dir/file2
/dir
Обратите внимание, что имя каталога стоит ПОСЛЕДНИМ! Это правиль-
но. Ключ -depth переворачивает список файлов так, что имя каталога пе-
чатается последним.
Что это дает? Фокус в том, что сначала копируются file1 и file2, а
затем устанавливаются права доступа данного каталога. Вы можете за-
писать файлы в каталог, для которого вы не имеете права записи. Благо-
даря тому, что файлы копируются первыми, вы можете не беспокоиться о
том, какого рода права доступа имеет этот каталог. К сожалению, ключ
-depth команды find поддерживается не всеми версиями системы UNIX.
3.2.2. can - удаление файлов в "мусорную корзину"

Имя: can
______________________________________________________________________
can Управление "мусорной корзиной" файлов
НАЗНАЧЕНИЕ
Перемещает файлы в "мусорную корзину", симулируя их удаление. Это
допускает восстановление файлов после их кажущегося удаления.
Формат вызова
can [-l] [-r] file [file ...]
Пример вызова
can junk Посылает файл junk в "мусорную корзину"
Исходный код для can
1 :
2 # @(#) can v1.0 Maintain file trash can Author: Russ Sage
4 CAN=$HOME/.trashcan
6 if [ ! -d $CAN ]
7 then mkdir $CAN
8 fi
10 if [ "`echo \"$1\"|cut -c1`" = "-" ]
11 then case $1 in
12 -l) echo "$CAN:"
13 ls -al $CAN
14 exit 0;;
15 -r) echo "removing $CAN/*:"
16 rm -rf $CAN/*
17 exit 0;;
18 -z|-?) echo "usage can [-l] [-r] file [file ...]" "&2
19 exit 0;;
20 esac
21 fi
23 mv $@ $CAN
Переменные среды выполнения
CAN Положение каталога "мусорной корзины"
HOME Положение вашего регистрационного каталога
Описание
Зачем нам нужен can?
По большому счету система UNIX, при всем ее великолепии, является
просто структурой для накопления и манипулирования данными в файлах.
Как мы отмечали раньше, эта система включает сотни файлов. Некоторые
файлы вы желаете хранить неопределенно долго, в то время как другие
отслужили свое и создают беспорядок на диске. К несчастью, легко выб-
росить то, что в действительности вы хотели сохранить. Команду rm со-
вершенно не украшает то, что она является печью для сжигания мусора:
бросьте что-нибудь в нее и оно пропадет (если только вы не имеете ко-
пии, а восстановление копии - это трудоемкая работа). Вот несколько
классических примеров неверного применения команды rm:
rm * /tmp "-- Удалить все файлы в каталоге /tmp
Мы хотели сказать rm /tmp/*, а на самом деле произошло сначала
удаление всех файлов в текущем каталоге, а затем попытка удалить /tmp.
Последнее будет безуспешным, поскольку tmp - это каталог. В результате
мы удалили все, что хотели сохранить, и сохранили все, что хотели уда-
лить! Этот синтаксис похож на другие операторы UNIX, вроде "grep *
file": противная ошибка.
rm -rf / tmp "-- Удалить каталог tmp со всеми файлами
Мы хотели сказать rm -rf /tmp, но нечаянно вставили пробел в ко-
манду. На самом деле удалятся ВСЕ файлы во всей системе (если мы дадим
команде выполняться достаточно долго), потому что мы сказали UNIX уда-
лить корневой каталог и всех его потомков! Вы должны быть внимательны с
командой rm. Если покажется, что что-то не так, удалите эту команду.
Она может погубить вас.
Одна такая ошибка может испортить вам целый день. После того, как
это случится, вы станете осторожным на некоторое время, потом внимание
ослабнет. Если вы не будете бдительным, ошибки вернутся, чтобы пресле-
довать вас.
Для нас "мусорная корзина" более желательна, чем печь для сжигания
"мусора". Используя этот путь, вы можете вернуться и восстановить то,
что вы выбросили по ошибке. Вы также хотели бы контролировать, когда
появится мусоросборщик, захватит и окончательно удалит "мусор". Вы мо-
жете периодически просматривать содержимое "мусорной корзины", а затем
очищать корзину, когда вы уверены, что вы не хотите ничего в ней сохра-
нять. Нельзя допускать, чтобы корзина была слишком заполнена, потому
что она занимает дисковое пространство.
Что делает can?
Командный файл can предназначен для управления "мусорной корзиной"
ваших файлов. Используя утилиту, вы можете свести к минимуму случайные
потери во время работы и даже впоследствии восстанавливать файлы при
необходимости.
Can не только помещает ваши файлы в "мусорную корзину", но и пока-
зывает вам, что в ней в настоящее время находится и очищает ее, когда
вы этого хотите.
Can распознает только ключи -l и -r. Ключ -l показывает, что нахо-
дится в "мусорной корзине", а -r удаляет все ее содержимое. Запомните,
что если вы что-то удалили из "мусорной корзины", вы не сможете его
восстановить.
Процесс помещения файлов в "мусорную корзину" выполняется командой
mv. Ключи, предназначенные для can, должны быть первым аргументам в ко-
мандной строке. Если вы желаете передать ключи команде mv, то их можно
поместить в любом месте командной строки. Единственные ключи, дающие
синтаксическую подсказку, - это -z и -?. Их предназначение - быть фла-
гами только для обработки ошибок. Благодаря наличию специальных флагов
обработки ошибок, выдающих справочную (help) информацию, ключи команды
mv, как и ключи can, можно помещать первыми в командной строке, не ока-
зывая влияния на can. Если вы создаете ваши командные файлы так, чтобы
эти ключи всегда выдавали информацию об использовании (т.е. никогда не
были "настоящими" ключами), то вы имеете хороший способ получения помо-
щи по синтаксису. Многие (но, увы, не все) стандартные команды UNIX да-
ют по ключам -z или -? подсказку об использовании и это полезно помнить
всякий раз, когда вы попали в тупик.
Если can не получает никаких ключей, действие по умолчанию заклю-
чается в пересылке всех указанных файлов в "мусорную корзину", разме-
щенную в вашем регистрационном каталоге под именем $HOME/.trashcan.
Если этот каталог отсутствует, он автоматически создается при первом
выполнении командного файла can. Это позволяет вам запускать команду,
не указывая специального положения "корзины". Если вы применяете ключ
-r, файлы в "мусорной корзине" будут удалены, а сама она нет.
Примеры
1. $ can *.c
Перемещает все файлы, которые оканчиваются на .c, в "мусорную кор-
зину".
2. $ can -l
Выдает список всех файлов, размещенных сейчас в "мусорной корзи-
не".
3. $ can -r
Удаляет все файлы из "мусорной корзины".
4. $ can -q *
Передает ключ -q команде mv. Поскольку это недопустимый ключ ко-
манды mv, она выдает сообщение об ошибке и завершается.
Пояснения
Строка 4 устанавливает место "мусорной корзины" так, чтобы она
размещалась в вашем регистрационном каталоге под именем .trashcan. За-
метьте, что ее именование, начиная с точки, делает ее нераспечатывае-
мым, или скрытым файлом. Единственный способ увидеть такие файлы -
использовать ключ -a в команде ls.
Строки 6-8 проверяют, определен ли сейчас каталог "мусорной корзи-
ны". Если нет, он создается. Обратите внимание, что поскольку его соз-
даете вы, он имеет такие права доступа на чтение и запись, как в вашем
регистрационном каталоге.
Строки 10-21 проверяют, начинается ли первый позиционный параметр
с черточки (-). Если такой параметр обнаружен, проверяется, является ли
он ключом командного файла can (-l, -r, -z или -?). Обратите внимание,
что для того, чтобы для использования двойных кавычек внутри двойных
кавычек (строка 10), вы должны экранировать кавычки. Символ обратной
косой черты (\) использован именно для этой цели.
Если указан ключ -l, выдается напоминание об имени каталога
"мусорной корзины", команда ls выводит список файлов в "мусорной корзи-
не" и процедура can завершается, поскольку требовалось только вывести
список.
Если указан ключ -r, выдается сообщение об имени каталога очищае-
мой "мусорной корзины" и файлы в ней удаляются командой rm. Это разру-
шительная вещь и удаляет ваши файлы навсегда. После удаления can завер-
шает работу. Вы можете дополнить программу процедуры так, чтобы давать
подтверждение перед выполнением команды, если это позволит вам чувство-
вать себя более спокойно.
Если указан ключ -z или -?, выдается подсказка об использовании и
can завершается. Это не совсем хорошо, но мы не можем использовать сим-
вол *, соответствующий любому другому ключу, поскольку ключ может быть
предназначен для команды mv, а не для can. Благодаря использованию все-
го двух аргументов для обработки ошибок, мы можем разрешить передачу
всех остальных аргументов. Если ключ не является одним из ключей can,
или одним из указанных ключей обработки ошибок, то он передается коман-
де mv. Если ключ недопустим для этой команды, команда mv выдает свое
сообщение об ошибке и завершает работу. Вы можете, естественно, модифи-
цировать командный файл так, чтобы он проверял допустимость ключей ко-
манды mv на "внешнем" уровне. Тогда он может выдать сообщение об ошибке
и завершиться, если указанный ключ недопустим ни для can, ни для mv.
Вопрос в том, стоит ли платить за более полный контроль над обработкой
ошибок ценой разбухания программы и временем исполнения.
Строка 23 выполняет собственно перемещение файлов в "мусорную кор-
зину". Заметьте, что это выполняется только если не указаны никакие
ключи can, поскольку это поведение can, принятое по умолчанию. Здесь
используется параметр $@. Путем включения всех параметров в командную
строку, любые ключи, предназначенные команде mv, передаются ей. Таким
способом мы можем изменить путь, которым файлы посылаются в "мусорную
корзину".
3.2.3. dosflp - копирование файлов с гибкого диска формата MS-DOS
с использованием символов шаблона в именах файлов

Имя: dosflp
_____________________________________________________________________
dosflp Копирование файлов с гибкого диска формата DOS с
использованием символов шаблона в именах файлов
НАЗНАЧЕНИЕ
Копирует файлы с гибкого диска в формате DOS (в XENIX) на жесткий
диск. Обеспечивает возможность использования записи с помощью симво-
лов-шаблонов для имен файлов на гибком диске, где такая запись обычно
недопустима.
ФОРМАТ ВЫЗОВА
dosflp [-a] [-c] [-dDRIV] [-eEXP][-h] [-l] [-r] [-sDIR]
где
-a означает копирование файлов, соответствующих *.asm
-c означает копирование файлов, соответствующих *.c
-d выбирает имя устройства DRIV из набора A,B,X,Y (по умолчанию A)
-e использует выражение EXP, чтобы применить к файлам grep
-h копирует файлы, соответствующие *.h
-l только выдает список файлов
-r удаляет файлы вместо их копирования
-s указывает подкаталог DIR на гибком диске формата DOS
Пример вызова
dosflp Копирование всех файлов с устройства A: в текущий
каталог
Исходный код для dosflp
1 :
2 # @(#) dosflp v1.0 Wildcard copies from DOS floppy
Author: Russ Sage
4 EXP=.\*
5 DRIVE="A:"
6 OP="c"
8 if [ "$#" -gt 0 ]
9 then for ARG in $@
10 do
11 case "$ARG" in
12 -a) EXP='.*\.asm$';;
13 -c) EXP='.*\.c$';;
14 -d*) DRIVE="`echo $ARG | cut -c3-`:";;
15 -e*) EXP='`echo $ARG | cut -c3-`';;
16 -h) EXP='.*\.h$';;
17 -l) OP="l";;
18 -r) OP="r";;
19 -s*) DRIVE="$DRIVE`echo \"$ARG" | cut -c3- `/";"
20 *) echo "dosflp: arg error"
21 echo "usage: dosflp [-a] [-c] [-d] [-e] [-h] [-l]
[-r] [-s]"
22 exit 1;;
23 esac
24 done
25 fi
27 case $OP in
28 c) echo "\nCopying files from $DRIVE to `pwd`";;
29 l) echo "\nListing files on $DRIVE"
30 dosdir $DRIVE | more
31 exit;;
32 r) echo "This option removes all the data on the floppy."
33 echo -n "Do you want to do this (y/n): "
34 read RSP
35 if [ "$RSP" = "y" ]
36 then echo "\nRemoving files on $DRIVE"
37 else exit
38 fi;;
39 esac
41 dosls $DRIVE | tr "[A-Z]" "[a-z]" " /tmp/doslist
43 for FILE in `grep "$EXP" /tmp/doslist`
44 do
45 echo $FILE
46 case $OP in
47 c) doscp $DRIVE$FILE .;;
48 r) dosrm $DRIVE$FILE;;
49 esac
50 done
52 rm /tmp/doslist
Переменные среды выполнения
ARG Хранит аргументы командной строки
DRIVE Устройство с гибким диском формата DOS
EXP Выражение, имитирующее действие символа-шаблона
FILE Хранит имя файла, над которым производится действие
OP Ключ, определяющий необходимое действие
Описание
Зачем нам нужен dosflp?
Это команда только для системы XENIX. Операционная система XENIX,
являясь продукцией фирмы Microsoft, имеет средства для общения с файло-
вой системой MS-DOS. Для ознакомления с основами совместного использо-
вания DOS и XENIX давайте рассмотрим основные параметры.
Каждый жесткий диск может иметь максимум четыре раздела. Это огра-
ничение MS DOS, которое перенесено в мир XENIX. Ничего плохого в этом
нет, пока мы не начинаем работать с жестким диском большой емкости. Для
70-мегабайтного диска, например, вы можете создать четыре рав-
ных раздела, каждый из которых содержит приблизительно 17 Мбайт. Вы мо-
жете создать меньший раздел, но тогда другой раздел должен быть больше.
В зависимости от того, какая часть ваших программ и данных должна быть
использована в основном MS-DOS и какая - XENIX, может быть использована
различная конфигурация.
Большим преимуществом системы XENIX/DOS является то, что XENIX мо-
жет размещаться в одном разделе, а DOS в другом. Как это сделать? Нужно
запустить программу "fdisk" в каждой операционной системе. Это значит,
что XENIX может общаться с разделом DOS, получая полное имя устройства,
указывающее на другой раздел. Драйвер, который читает раздел DOS, дол-
жен знать, как выглядит DOS (т.е. знать файловую систему DOS). Если вы-
полнить такую операцию, можно получать списки файлов и копировать их
туда и обратно. К сожалению, DOS не имеет возможности чтения разделов
XENIX.
При работе с гибким диском вы имеете дело только с одним разделом.
Это снова ограничение DOS. Некоторые системы UNIX, в отличие от DOS,
позволяют иметь столько разделов на жестком или гибком диске, сколько
вы хотите, в отличие от DOS. По определению, гибкий диск DOS сформати-
рован в системе DOS, которая выполняет форматирование низкого уровня и
помещает файловую систему DOS на гибкий диск.
В системе XENIX гибкий диск может быть либо в формате файловой
системы, либо неструктурированным устройством последовательного доступа
подобно магнитной ленте. Для процедуры dosflp мы используем только гиб-
кие диски в формате DOS.
Теперь к делу. Предположим, вы имеете систему DOS и файлы, находя-
щиеся на диске DOS, вы можете читать и писать файлы на гибкий диск из
XENIX. Но существуют некоторые ограничения на выполнение операции копи-
рования, которые не слишком удобны пользователю. Например, вы можете
сказать "doscp *.c a:". В результате все файлы текущего каталога кото-
рые оканчиваются на .c, будут скопированы на гибкий диск формата DOS на
устройстве a:. Побочный эффект выполнения doscp заключается в том, что
все символы перевода строки (или прогона строки) превращаются в символ
возврат каретки/перевод строки, поскольку DOS обрабатывает конец строки
иначе, чем XENIX. Таким же образом, когда вы копируете с гибкого диска
формата DOS в XENIX, лишние символы возврата каретки убираются.
Что вы не можете сделать, так это сказать "doscp a:*.c". Команда
doscp не допускает указания вида *.c при копировании с гибкого диска.
Это происходит потому, что командный процессор распространяет метасим-
волы (*,?,[]) и не может непосредственно читать раздел DOS. Поэтому вы
не можете использовать символы при копировании с гибкого диска DOS.
Отметим, что может наблюдаться гораздо больше побочных эффектов,
когда вы имеете дело с гибкими дисками DOS. Во-первых, длина имени фай-
ла ограничена. DOS допускает до восьми символов имени файла плюс три
символа расширения. В результате после копирования всех ваших файлов
XENIX на гибкий диск многие из них могут иметь не те имена, которые они
имели в XENIX. Это сущее страдание, когда вы пытаетесь сделать копию на
гибкие диски DOS, потому что вы больше не имеете уверенности, как обра-
щаться к файлам, когда вы копируете их обратно с гибкого диска. Кроме
того, поскольку расширение имени файла в DOS имеет только три символа,
файл с именем "spreadsheet.finance" может оказаться на гибком диске DOS
с именем "spreadsh.fin" и распознавание его может представлять опреде-
ленные трудности.
Но это еще не все. Когда файл копируется из XENIX в DOS, ВСЕ имена
в DOS записываются заглавными буквами. Если у вас есть файлы с именами,
в которых смешаны верхний и нижний регистры, то вы несколько потеряете
понятность имен. Если вы используете в именах символы верхнего регистра
при копировании файлов обратно в XENIX, они не переводятся на нижний
регистр. В результате все имена ваших файлов оказываются записанными
символами верхнего регистра в XENIX, что не очень удобно.
В чем мы нуждаемся, так это в таком средстве, которому мы можем
указывать, какие файлы копировать с гибкого диска на жесткий диск, и
которое копирует их с сохранением регистра в имени файла. Все это дела-
ет процедура dosflp.
Что делает dosflp?
Dosflp пытается исключить все негативные аспекты копирования фай-
лов XENIX/DOS. Это высокое требование, но оно достижимо. Вкратце подход
dosflp следующий: получить список имен файлов с гибкого диска, пере-
вести имена в нижний регистр, выбрать из полного списка имена тех фай-
лов, которые соответствуют вашим требованиям, и затем копировать файлы
один за другим в текущий каталог XENIX. Для того, чтобы сделать это,
требуется гораздо больше команд XENIX вида dosxx, а также различных
других команд XENIX.
В дополнение к копированию, dosflp также выдает список файлов, ко-
торые имеются на гибком диске DOS, и удаляет файлы с гибкого диска. Эти
функции легко реализовать, потому что как только один раз процедура
доступа написана, добавить новые команды для выполнения операций над
файлами довольно просто.
Обычно мы хотим управлять файлами определенного типа как группой.
Сюда относятся ассемблерные исходные файлы, исходные файлы на языке C и
файлы-заголовки на языке C. Поэтому, чтобы снять с вас обязанности по
вводу универсальных символов для этих типов файлов, мы прямо указываем
их в качестве опций команды dosflp. Например, ключ -a копирует только
файлы, которые оканчиваются на .asm, поэтому нет необходимости помнить
вид выражения для копирования этих файлов. Аналогично, ключ -c копирует
все файлы, оканчивающиеся на .c, и ключ -h копирует файлы, оканчивающи-
еся на .h.
Как мы увидим позже, прямое указание, о котором мы говорим здесь,
являеется выражением для команды grep. Использование всех возможностей
команды grep достигается при указании образцов имен файлов.
Используйте ключ -d для указания, с какого гибкого диска произво-
дится копирование. По умолчанию это устройство a: или A:. Не имеет зна-
чения, на каком регистре вы укажете имя устройства. Для уверенности
проверьте файл /etc/default/msdos. Этот файл содержит соответствия меж-
ду символом устройства и маршрутным именем XENIX. Например, файл может
выглядеть так:
A=/dev/fd048ds9
B=/dev/fd148ds9
C=/dev/hd0d
D=/dev/hd1d
X=/dev/fd096ds15
Y=/dev/fd196ds15
Как вы видите, маршрутные имена - это обычные имена устройств и
ничего больше.
В качестве основного средства выполнения работы dosflp использует
команду doscp. Это утилита способна понимать формат файловой системы
DOS. Dosflp передает ей обозначение устройства и другие опции
посредством переменных командного процессора. Например, ключ "-dB:" ме-
няет устройство на B вместо принятого по умолчанию устройства A.
Если выражения прямого указания типа файлов не соответствуют тому,
что вам нужно, вы можете определить свои собственные выражения со-
поставления, используя ключ -e. Напомним, что выражение должно соот-
ветствовать синтаксису команды grep. Если вы хотите освежить свою па-
мять, посмотрите grep(1) в руководстве по AT&T UNIX или grep(C) в руко-
водстве по XENIX. Для получения полной информации о синтаксисе посмот-
рите ed(1). Этот синтаксис является основой большинства команд, работа-
ющих с регулярными выражениями, таких как sed и grep.
Например, если вы используете выражение "*test*", выражение для
grep должно иметь вид ".*test.*". Его можно слегка изменить в зависи-
мости от того, что вы желаете иметь с каждой стороны цепочки test. В
данном случае синтаксис указывает все символы (.*), за которыми следует
цепочка t-e-s-t, а затем любая цепочка символов (.*). В этом случае
ключ имел бы вид "-e.\*test.\*". Это кажется немного странным, но это
соответствует синтаксису. (Двойные кавычки не являются частью команды.)
Символ обратной косой черты (\) используется для экранирования звездоч-
ка. Если вы не экранируете ее, командный процессор соотнесет ее с име-
нами всех файлов вашего текущего каталога, чего вы не желаете. Экрани-
рование ее позволит, чтобы нужный символ был передан dosflp, для
использования ее в grep-последовательности.
Ключ -h - это еще один из ключей прямого указания. Давайте вкратце
рассмотрим его синтаксис внутри dosflp. Это ".*\.h$", и он указывает
любой символ, за которым стоит одно или несколько вхождений любого сим-
вола (.*), литеральная точка (.\), символа h и вслед за ним конец стро-
ки (h$). Вы могли бы указать то же самое, используя ключ -e, но -h де-
лает это гораздо легче.
Ключ -l изменяет основное действие команды dosflp. Вместо копиро-
вания файлов он выдает список файлов. Это делается путем выполнения
различных команд вида dosxx, в данном случае dosdir. Ключ выдачи списка
полезен в dosflp, потому что вы можете получить список как информацию к
решению о том, что делать дальше, и вам нет необходимости помнить ко-
манду dosdir.
Ключ -r также изменяет основную операцию команды dosflp. В этом
случае файлы удаляются, а не копируются. Если вы указали этот ключ, вы-
дается сообщение, которое просит вас подтвердить, что вы хотите удалить
указанные файлы. Вы можете просто ответить "n", и запретить удаление,
если вы ввели этот опцию случайно. Напомним, что удаленные файлы или
файлы, включенные в список (в случае ключа -l), выбраны выражением
grep, которое жестко запрограммировано или указано пользователем. По
умолчанию выбираются ВСЕ файлы. Для ключа -r это соответствует тому,
что сказать "rm *".
Последний ключ, -s, обеспечивает возможность доступа к файлам, ко-
торые размещены внутри подкаталога на гибком диске DOS. Если вы обраща-
етесь только к имени устройства, по умолчанию ключ -s относится к ката-
логу самого верхнего уровня на гибком диске. Если нужный вам файл нахо-
дится в подкаталоге, вы должны использовать определенную нотацию, чтобы
попасть в него. Одно из различий между XENIX и DOS заключается в симво-
ле, используемом для разделения элементов маршрутного имени. XENIX
использует обычную запись в стиле UNIX - /x/y/z. В DOS применяется сим-
вол "обратная косая черта", т.е. \x\y\z. Но если вы хотите использовать
команды XENIX на гибком диске DOS, вы должны применять обычную запись
XENIX, a:/x/y/z. Это не совсем понятно, но правильно.
По умолчанию, dosflp копирует файлы с гибкого диска в
ваш текущий каталог на жестком диске. Если вы измените операцию на
выдачу списка или удаление, эта операция будет произведена на гибком
диске.
Примеры
1. $ dosflp -dB: -c -l
Выдает список всех файлов вида *.c на гибком диске DOS, размещен-
ном в устройстве B. В этом случае не происходит переход вниз в подката-
логи, а включаются лишь файлы, размещенные на верхнем уровне каталогов.
2. $ cd /destdir
$ dosflp -ssrc -e.\*src.\*
Переход в каталог, куда будут помещены файлы. Копируются файлы с
гибкого диска DOS (устройство A, подкаталог src), в текущий каталог.
Файлы для копирования указаны как *src*. В записи UNIX это выглядело бы
так: "cp A:/src/*src* .".
3. $ dosflp -r -stmp
Удаляет все файлы, размещенные в подкаталоге tmp на гибком диске
DOS (устройство A). Обратите внимание, что сам каталог не удаляется. В
записи UNIX это выглядело бы так: "rm A:/tmp/*".
4. $ sh -x `path dosflp` -dB:
Запускает процедуру dosflp в отладочном режиме выполнения.
Единственное ограничение при таком вызове командного процессора заклю-
чается в том, что файл данных, который вы посылаете ему (в данном слу-
чае dosflp), должен иметь полное маршрутное имя. Поскольку командный
процессор НЕ выполняет поиск маршрутного имени файла, нам необходимо
сперва найти маршрутное имя dosflp, затем передать его командному про-
цессору, запущенному в отладочном режиме выполнения, а также передать
процедуре dosflp аргумент в командной строке. Заметьте, что вызов
dosflp таким путем не меняет значение переменной $#, которое только
распознает ключ -dB: как аргумент.
Пояснения
Строки 4-6 выполняют инициализацию по умолчанию путем сохранения
значений в соответствующих переменных командного процессора. По умолча-
нию символ-шаблон ставится в соответствие всем файлам, указанным выра-
жением для команды grep .\*. Обратная косая черта требуется для экрани-
рования звездочки, поэтому она не перехватывается командным процессо-
ром. Устройство по умолчанию - A:. Операция по умолчанию - копировать
файлы, что указано значением "c" для переменной опции.
В строках 8-25 устанавливаются значения ключей и производится про-
верка на наличие ошибок. Если командная строка имеет некоторые аргумен-
ты ($# -gt 0), мы перебираем каждый аргумент и проверяем его. Если най-
ден допустимый ключ, переменные устанавливаются согласно ключу. Если
обнаружен недопустимый ключ, выдается сообщение об ошибке и программа
завершается с плохим статусом возврата.
Имеется два важных типа ключей. Ключи, которые выполняют прямое
указание типа файла, просто устанавливают переменную EXP в соответствии
с ключом. Аналогично, ключи, которые определяют, какой вид работы будет
выполняться процедурой, просто устанавливают соответствующую переменную
OP. Другие ключи должны обрабатываться путем извлечения одного или
нескольких символов из командной строки, которые следуют за флагом клю-
ча, эхо-отображения и конвейерной пересылки текущего аргумента ARG ко-
манде cut для извлечения символа (символов), начинающихся с третьего
символа аргумента, затем присвоения результата этой операции соот-
ветствующей переменной.
Из всего сделанного следует вывод, что пробелы между ключами и
символами, которые стоят за ними, не допускаются. Например, ключ -d
должен получить имя устройства. По синтаксису должно быть -dB:, но не
-d B:, потому что B: интерпретировалось бы как другой аргумент ARG в
цикле for, а это все испортит.
В строках 27-39 операция, которая должна быть выполнена, определя-
ется при помощи следующего оператора case. Если должно быть выполнено
копирование, выдается сообщение "copying" и выполняется то, что следует
за оператором case. Если должен быть выдан список файлов, выдается
сообщение об устройстве, содержимое которого должно распечататься, за-
тем выдается список файлов путем выполнения команды dosdir и конвейер-
ной пересылки результата команде more, после чего dosflp завершается.
Если файлы должны быть удалены, пользователю выдается запрос на
подтверждение удаления. Если ответ "yes", выдается сообщение, с какого
устройства файлы будут удалены. Если ответ "no", dosflp завершается.
Остаток командного файла имеет дело с механизмом копирования.
Строка 41 - это первый шаг в наведении моста над пропастью между двумя
типами носителей. Команда dosls использована для получения полного
списка файлов с гибкого диска. Перед тем как мы передадим этот список
во временный файл, мы пропустим его через команду tr (translate), кото-
рая преобразует все символы на нижний регистр, чтобы при копировании
файлов их имена были в нижнем регистре. В результате копии будут поме-
щены на диск XENIX с именами файлов в нижнем регистре. Если у вас есть
файлы с именами в верхнем регистре или в смеси регистров, вы должны
вручную исправить их после копирования.
Строки 43-50 выполняют само копирование. Цикл for запускается для
доступа к каждому файлу индивидуально. Это требование команд вида
dosxx. Вы должны получать доступ к одному файлу один раз, поскольку
этот уровень не обладает возможностью указания символа-шаблона. Имена
файлов, которые использует цикл for, определены путем использования ко-
манды grep для выбора имен соответственно выражению, установленному ра-
нее.
Имя каждого выбранного файла сначала отображается, так что пользо-
ватель может видеть, выполняется ли команда так, как ожидалось. В этом
месте мы можем сделать одну из двух вещей: или копировать файлы, или
удалить их. Эта операция определяется оператором case в строках 46-49.
Если операция - копирование файлов, файлы копируются из комбинации уст-
ройство-файл в текущий каталог. Обратите внимание, что в переменную
DRIVE включается подкаталог, если он был указан в командной строке. Это
объясняет наличие символа "/" в конце присвоения значения переменной
DRIVE в строке 16. Полное выражение должно быть таким: B:/subdir/file.
Если операция - удаление файлов, комбинация устройство/файл удаляется
выполнением команды dosrm. Попутно заметим, что маршрутное имя есть
нечто гибкое (или небрежное, в зависимости от того, как вы смотрите на
него) в том смысле, что вы можете сказать A:/subdir или A:subdir. Оба
варианта правильны. После того как все файлы будут обработаны, времен-
ный файл удаляется.
ВОЗМОЖНЫЕ МОДИФИКАЦИИ КОМАНДНОГО ФАЙЛА
Одно из мест, где вы можете настраивать dosflp, это регулярные вы-
ражения. Уже включены выражения для .asm, .c и .h, но вы можете изме-
нить это или добавить больше ключей для любой последовательности, кото-
рую вы часто используете.
3.3. Средства получения резервных копий
3.3.1. autobkp - автоматичеески наращивамый файл резервной копии

Имя: autobkp
_____________________________________________________________________
autobkp Автоматически наращиваемый файл резервной копии
НАЗНАЧЕНИЕ
Производит поиск по дереву файлов, которые изменялись за последние
24 часа, и пересылает их в другую систему (посредством uucp) или пере-
мещает их в другую область жесткого диска.
ФОРМАТ ВЫЗОВА
autobkp [-c] ["logfile]
-c копирует файлы в другое место диска вместо
использования uucp
Пример вызова
autobkp " filelist "" bkplog
Копирует все файлы, указанные в filelist,
и записывает имена файлов в файл с именем bkplog
Командный файл autobkp
1 :
2 # @(#) autobkp v1.0 Automatic file backup Author: Russ Sage
4 if [ $# -gt 1 ]
5 then echo "autobkp: argument error" "&2
6 echo "usage: autobkp [-c] ["logfile]" "&2
7 exit
8 fi
10 if [ "$1" = "-c" ]
11 then COPY=on
12 else COPY=off
13 fi
15 echo "\nBACKUP DATE `date '+%a %m/%d/%y %H:%M:%S'`"
16 echo "
"
18 SYSTEM='' # destination system uucp node name
19 : ${SYSTEM:=`uuname -l`}
21 echo "Sourse system:\t\t`uuname -l`\nDestination system:\t$SYSTEM"
23 while read SRCDIR DESTDIR FILES
24 do
25 if [ ! -d $SRCDIR ]
26 then echo "autobkp: $SRCDIR is not a directory"
27 continue
28 fi
30 cd $SRCDIR
31 echo "\nFinding files in: $SRCDIR"
33 for FILE in `find . -type f -ctime 0 -name "$FILES" -print`
34 do
35 case $COPY in
36 off) uucp $FILE $SYSTEM!$DESTDIR;;
37 on) cp $FILE $DESTDIR;;
38 esac
39 echo " Transferred $FILE to $DESTDIR"
40 done
41 done
Переменные среды выполнения
COPY Флаг, определяющий, используется команда uucp или cp
FILE Имя каждого файла, найденного в исходном списке маршрутов
FILES Символ-шаблон, указывающий, какие файлы определены
PATH1 Имя маршрута-источника
PATH2 Имя маршрута-приемника
SYSTEM Имя системы-приемника для uucp
Описание
Зачем нам нужен autobkp?
Как мы заметили, файлы в UNIX плодятся как кролики. Чем больше
файлов мы создаем, тем сильнее желание сохранять их упорядоченными. До-
вольно легко стать ленивым или получить ложное представление о безо-
пасности и пренебречь регулярным копированием.
Вы можете подходить к копированию файлов несколькими путями. Наи-
более популярной стратегией является выполнение наращиваемого копирова-
ния, когда вся система копируется с некоторой начальной даты (и иногда
повторно с регулярными интервалами, но не часто). При коротких интерва-
лах (обычно ежедневно) файловая система проверяется на наличие файлов,
которые были модифицированы или добавлены за последние 24 часа. Такие
файлы копируются, поэтому копия в целом поддерживается такой, какой яв-
ляется система в настоящее время.
Где размещать копируемые файлы - это еще один интересный вопрос,
зависящий от конфигурации вашей системы, количества доступного прост-
ранства и важности данных. Давайте рассмотрим некоторые возможности.
Автономная микро- или супермикросистема может иметь всего один
жесткий диск. Если диск содержит достаточно места для размещения друго-
го раздела, вы можете копировать в этот раздел. Раздел может также
использоваться как неструктурированное устройство в отличие от файловой
системы и рассматриваться как магнитная лента или гибкий диск. Среди
других возможностей хранения информации могут быть второй жесткий диск,
кассетная лента или устройство копирования на ленту. Если вам недоступ-
на ни одна из этих возможностей, вы всегда можете копировать на гибкие
диски. Это утомительная ручная работа, но она может быть выполнена при
помощи команд tar или cpio.
Если вы также имеете доступ к другой, большей системе, такой как
общий главный компьютер, вы можете копировать файлы, посылая их в эту
систему посредством команды uucp. Даже если вы имеете достаточно места
в вашей собственной системе для сохранения ваших копий, у вас может
быть очень сильное желание послать копии всех важных файлов в главную
машину, потому что это даст вам выносную копию за пределами вашего
места расположения. Пожары, наводнения и др
ются.
Нам необходим механизм, который обычно запускается автоматически
(по команде cron или с помощью процедуры at, описанной в главе 5). Сна-
чала он обнаруживает все файлы, которые были изменены в последние 24
часа (надеемся, что вы уже имеете первоначальную копию всего). Он начи-
нает искать файлы из указанных каталогов и копировать подходящие файлы
в указанные каталоги-приемники. Он копирует файлы, используя утилиты,
которые наилучшим образом соответствуют используемой вами конфигурации.
Все эти вещи выполняются нашим командным файлом autobkp.
Что делает autobkp?
Вы перечисляете маршруты и autobkp находит файлы по этим маршрутам
и копирует их в то место, которое вы указали. Вы можете указывать имена
файлов по образцам, таким как *.c, *.h или каким-либо еще. С помощью
autobkp вы можете копировать важные файлы без копирования всех файлов.
Иногда это удобно - пропускать файлы при копировании. Типичные файлы,
которые вы, возможно, не хотите копировать, - это очень большие файлы
(не являющиеся важными, как файл core и файлы данных), временные файлы
(как *.o, которые вновь создаются при каждой новой компиляции) и испол-
няемые файлы, если у вас есть исходные программы на языке Си и вы може-
те их скомпилировать для получения новых исполняемых файлов. Пропуская
эти файлы, вы можете уменьшить размер ваших копий на мегабайты.
По умолчанию копирование производится командой uucp, которая пред-
полагает, что у вас подчиненная система по отношению к главной машине и
копирует ваши файлы в большую систему. Если вы хотите копировать ваши
файлы в другое место жесткого диска или на другой жесткий диск, исполь-
зуйте ключ -c для копирования командой cp вместо использования команды
uucp.
Во время процесса копирования на стандартный вывод выводятся сооб-
щения о состоянии дел. Это позволяет легко собрать все сообщения путем
переадресации stdout на время копирования. Если вы выполняете autobkp
вручную, сообщения выводятся на экран. Первое сообщение - это заголо-
вок, который печатает день, дату и время. Это выглядит так:

| BACKUP DATE Fri 05/23/86 17:33:35
|
Второе сообщение определяет систему-источник и систему-приемник.
Оно появляется ниже. В нашем примере система-источник - russ, а систе-
ма-приемник - vax.

| Source system: russ
| Destination system: vax
При каждом входе в систему-источник выдается следующее сообщение:

| Finding files in: src_dir
|
где выражение src_dir - это место, откуда файлы будут переданы в
цикл копирования. Обратите внимание, что первое имя должно быть именем
каталога, потому что autobkp начинает именно с этого места поиск фай-
лов. Если первое имя не является каталогом, программа печатает сообще-
ние об ошибке и продолжает работу со следующим набором источник/прием-
ник для копирования.
Для каждого найденного файла печатается следующее сообщение после
завершения копирования:

| Transferred file to dest_dir
|
которое указывает, что файл file был скопирован в каталог-приемник
с именем dest_dir.
Файл со списком маршрутов
Чтобы сделать интерфейс настолько гибким, насколько это возможно,
autobkp читает стандартный ввод. Переназначая stdin, вы можете поддер-
живать разные списки файлов, которые необходимо копировать и переклю-
чать их в командной строке. Вы можете иметь один список маршрутов для
системных файлов, другой для исходных файлов, третий для личных файлов,
четвертый для файлов с готовым продуктом и так далее. Для каждой из
этих групп файлов создается список маршрутов и передается в качестве
входа для autobkp. Входные данные читаются как три поля: FROM, TO и
TYPE. Поле FROM - это каталог-источник. Поиск файлов начинается с этого
места. Напомним, что autobkp проходит вниз до конца дерева файлов, на-
чиная с указанного каталога.
Поле TO - это каталог-приемник, куда все файлы, найденные для дан-
ной записи в файле со списком маршрутов, помещаются на машине-приемнике
или в разделе-приемнике.
Поле TYPE - это описатель-шаблон, который сообщает autobkp, какие
файлы искать. Его значение может быть *, *.c, *src*, и так далее. Как
мы увидим позже, этот описатель передается команде find Unix, которая
фактически и выполняет поиск файлов. Вы можете использовать любое выра-
жение в поле TYPE, если оно соответствует синтаксису find.
Итак: все файлы, которые были изменены в последние 24 часа, обна-
руживаются в списке FROM с помощью описателя TYPE и копируются в об-
ласть TO.
Ниже приводится типичный файл со списком маршрутов. Он указывает
несколько каталогов, в которых производится поиск файлов. Обратите вни-
мание, что эти каталоги находятся под регистрационным каталогом: если
вы хотите скопировать ВЕСЬ регистрационный каталог полностью, вы можете
указать этот каталог, но здесь мы хотим выбрать только указанные ката-
логи.
/usr/russ/bin /pack1/russ/.bkp/bin *
/usr/russ/doc /pack1/russ/.bkp/doc *
/usr/russ/src /pack1/russ/.bkp/src *.c
/usr/product1 /pack1/russ/.bkp/product1 *.[ch]
Эти строки копируют каталоги bin, doc и src на локальной машине
автора. В случае каталога src мы указали, что копировать нужно только
исходные файлы на языке Си. Будет также скопирована некоторая полезная
информация из другого места этой же системы. Будут скопированы только
файлы с расширением *.c и *.h.
Место назначения (прямо указанное в командном файле автоматическо-
го копирования) - другая система UNIX. Место назначения - некоторый
смонтированный диск, регистрационный каталог, подкаталог копий (bkp).
Использование cron
Теперь, когда процедура autobkp знает, что искать, давайте скажем
ей, когда искать. Cron, вечный резидентный хранитель времени, может
легко выполнить эту работу. Входные данные для cron обычно устанавлива-
ются системным администратором (или кем-либо, кто имеет права записи в
/usr/lib/crontab), так что вы должны попросить администратора устано-
вить для вас вход в файл данных cron. Для получения дополнительной ин-
формации о входных данных cron, прочтите cron(1M) в Руководстве адми-
нистратора. Коротко говоря, полями в файле /usr/lib/crontab являются
минута, час, день месяца, месяц и день недели. Используя *, мы можем
установить принудительно многие из этих полей во все возможные значе-
ния. Входные данные для cron, копирующие мой регистрационный каталог в
4.00 утра каждый день каждой недели каждого месяца года, выглядят так:
0 4 * * * /usr/russ/bin/autobkp.cron
Обратите внимание, что вход в cron вызывает управляющую процедуру
вместо того, чтобы непосредственно использовать autobkp. Имеется
несколько важных причин, чтобы написать процедуру на базе утилиты
autobkp. Во-первых, cron не печатает диагностическую информацию на ваш
терминал, поэтому если что-нибудь идет не так, вы никогда об этом не
узнаете. Во-вторых, проще поддерживать усеченную версию autobkp, а зву-
ковые предупреждения добавлять в управляющую процедуру. Вы можете сде-
лать собственные модификации управляющей программы и не беспокоиться об
отсутствии сообщений от самой утилиты. Управляющую программу можно
настолько усложнить, насколько вы желаете. Представленная здесь вполне
работоспособна, но легко может быть дополнена.
# Cron-driven autobkp driver
echo "backed up: `date`" " /dev/tty00
/usr/bin/autobkp " /usr/russ/bin/autobkpath
"" /usr/russ/bin/autobkp.log
Этот драйвер выдает сообщение на терминал, запускает autobkp,
использует для ввода файл со списком маршрутов в каталоге bin и помеща-
ет все выводные сообщения в файл протокола. Отметим, что имя терминала
дано как абсолютное (tty00). Это правильно только в том случае, когда в
вашей системе имеется такой терминал. Использование этого имени терми-
нала позволяет сообщению появиться на экране даже если никто на нем не
зарегистрирован. Это хорошо, потому что первое, что вы сможете увидеть
утром на вашем экране - это сообщение. Если у вас нет указанного терми-
нала, вы можете сделать что-то другое, например, передачу самому себе
почтового сообщения.
Примеры
1. $ autobkp
Запускает программу без передачи ей файла со списком маршрутов и
без файла протокола. Поскольку поля FROM, TO, TYPE ищутся в стандартном
вводе, введите их вручную. Когда вы нажмете возврат каретки, autobkp
выполнит указанные действия, напечатает информацию на экран терминала и
будет ожидать дальнейшего ввода. Для завершения выполнения командного
файла введите ^d (в результате оператор read вернется с ненулевым ста-
тусом).
2. $ autobkp " pathlist
Получает все входные данные из файла со списком маршрутов, но пе-
чатает всю протокольную информацию на экран терминала. Autobkp заверша-
ется, когда прочитает все данные в файле pathlist.
3. $ autobkp "" logfile
Как и в первом случае, списки маршрутов должны быть введены с кла-
виатуры. Все выходные данные выводятся в файл протокола, а не на экран.
Для завершения autobkp введите ^d.
4. $ autobkp -c " pathlist "" logfile
Копирует файлы из одной области жесткого диска в другую (опреде-
ленную каталогом-приемником в файле pathlist). Берет все входные данные
из файла pathlist и выводит все выходные данные в файл logfile.
Пояснения
Строки 4-8 выполняют проверку на наличие ошибок. Autobkp может
быть вызван либо без указания опций, либо с одной опцией (-c, при
использовании cp). Вспомните, что переназначение ввода-вывода НЕ прини-
мается во внимание при рассмотрении аргументов, потому что командный
процессор интерпретирует символы переназначения и то, что следует за
ними, до вызова команды. Таким образом, если количество позиционных па-
раметров больше одного (#1 -gt 1), получаем ошибочное условие. Затем
выдается сообщение об ошибке и синтаксическая подсказка и программа за-
вершается.
В строках 10-13 проверяется использование ключа -c. Обратите вни-
мание, что мы не проверяем, равен ли параметр $# единице и не пытаемся
выделить первый символ, чтобы посмотреть, равен ли он "-". Это потому,
что такая проверка приведет к ошибке, если не указан никакой ключ (что
является верным синтаксисом, как указывалось ранее).
Если мы сказали
if [ $1 = -c ]
и не указали ключей, то команда проверки не сработает и будет вы-
дано сообщение о том, что "no argument in the statement" ("в операторе
нет аргументов"). Но если мы выполним экранирование, например, так:
if [ "$1" = "-c" ]
то кавычки допускают нулевое значение аргумента, так что проверка
правильно оценит недостающее значение $1 как "равен ли нуль -c?" Это
даст результат "ложь", поэтому все хорошо.
Попутно давайте внимательно рассмотрим работу команды проверки. Вы
можете выполнить проверку значения двумя способами. Первый - сравнение
строк, а второй - числовое сравнение. Переменные командного процессора
ВСЕГДА хранятся в виде строк. Вы можете, тем не менее, заставить систе-
му рассматривать эти последовательности как числа и интерпретировать их
значения как числовые, подобно оператору 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 печатает приглашение, ко-
торое направляется на другую сдвинутую строку полностью заполненного
экрана. Постоянство и скорость - вот чего мы добиваемся. Обучение таким
трюкам еще больше поможет вам при написании программ большого размера,
которые используют множество меню и другие текстовые выводы на экран.
Использование оператора 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 заставляет снова печатать главное меню
вместо того, чтобы пытаться выполнить какую-то наполовину определенную
операцию копирования.
18 fi
формацию, необходимую для ее обработки. Четырьмя основными командами
являются копирование, восстановление, выдача списка файлов и выдача
списка файлов с полной информацией. Если введена какая-то другая коман-
да, выдается сообщение об ошибке и главное меню снова выводится на эк-
ран. Единственный способ выхода из главного меню - это нажать возврат
каретки без какого либо-текста перед ним и строка 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-файлов.
Теперь, когда мы изучили, как распознавать и управлять файлами во-
обще, давайте рассмотрим некоторые систематические методы управления
ИНФОРМАЦИЕЙ в файлах. Мы начинаем в следующей главе с файлов, которые
важны для нас как для программистов.
* ГЛАВА 4. Управление программной документацией *
ВВЕДЕНИЕ
4.1. ПРОГРАММИРОВАНИЕ и УПРАВЛЕНИЕ ДОКУМЕНТАЦИЕЙ
4.2. ИЗВЛЕЧЕНИЕ ДОКУМЕНТИРУЮЩИХ ЗАГОЛОВКОВ
4.2.1. stripc - из файла на языке Си
4.2.2. stripf - из Си-функции
4.2.3. strips - из командного файла Shell
4.3. ctags - создание файла признаков исходного кода проекта
ВВЕДЕНИЕ
Вы решили рискнуть. Продукт на три месяца опаздывает в произ-
водство и нуждается лишь в крохотной доработке. Вы уверены, что знаете,
как работает функция, которая открывает входной буфер. Вы ее недавно
использовали. Вы увеличиваете размер буфера в вызове функции и запуска-
ете быстрый тестик. Все в порядке, поэтому вы окончательно собираете
поставку на диске и отправляете ее в производство. Месяц спустя, начи-
нают поступать сообщения от разгневанных заказчиков. Похоже, что если
текстовый процессор, электронная таблица и база данных открыты все
вместе и активны одновременно (что является одним из больших товарных
достоинств вашего продукта), то просто новый буфер настолько велик, что
поглощает ключевой раздел памяти и превращает высоко летающее чудо ин-
тегрированного программного обеспечения в яркую руину.
Почему вы не проверили документацию по этой функции? Выяснение то-
го, в каком файле находится документация, заняло бы определенное время,
а поскольку документацию так трудно сопровождать, то связанные с ней
вещи так или иначе устаревают. Тем не менее, аналогичные провалы не
должны возникать.
Программирование - тяжелая работа, но это только половина работы.
Хорошая документация очень важна, если вы собираетесь иметь возможность
сопровождать ваш программный код, но и управление всей документацией,
связанной с большим программным проектом также является тяжелой рабо-
той. Происходят постоянные изменения, и обычно отсутствует единообразие
подхода. Документирование исходных файлов на Сикак в целом, так и по
каждой функции является хорошим первым шагом, но такая документация не
очень полезна, если вы вынуждены пробираться через дюжины файлов, чтобы
обнаружить, как называется конкретная функция или какие функции состав-
ляют данный модуль.
Если вы хотели бы изучить еще одно средство, связанное с разработ-
кой, см. программу cg в главе 10.
4.1. ПРОГРАММИРОВАНИЕ И УПРАВЛЕНИЕ ДОКУМЕНТАЦИЕЙ
В данной главе представлен набор командных файлов командного про-
цессора для извлечения документирующей информации из исходного кода
программ на Си и командных файлов командного процессора. Используются
две стратегии. Первая состоит в том, что, следуя стандартной "модели
документации" в исходном коде, вы можете придумать командные файлы, ко-
торые просто "вытягивают" самые новые разделы с заголовочной информаци-
ей из файлов с исходным кодом и собирают их затем в новый файл. Такие
файлы служат в качестве каркаса для документации по программе. Следова-
тельно, при условии, что заголовки исходного кода изменяются разными
программистами стандартным образом, простая команда UNIX может извлечь
полностью новый каркас руководства.
Этот подход реализуют командные файлы stripc, stripf и strips.
Stripc и stripf предоставляют листинги блоков документации уровня файла
и уровня функций из ваших исходных файлов на Си, а strips извлекает доку-
ментацию из командных файлов командного процессора.
Второй подход - доступ к определенным видам структур (таким как
функции на Си) в теле самого программного кода. Этим методом вы можете
точно найти, как называется данная функция, без сосредоточенного изуче-
ния горы листингов. Командный файл ctags является и полезным инструмен-
том, и моделью применения этого подхода к другим видам программных
структур.
Ctags объединяет свой выводной файл с редактором vi/ex с целью
предоставления простого способа доступа к любой заданной функции и ее
просмотра, копирования или редактирования в текущей программе. Ctags
делает это путем предоставления признаков, которые понимает vi, для
каждой функции, обнаруженной в любом указанном наборе файлов. Таким об-
разом, вы можете использовать простую команду редактора, чтобы получить
то, что вам нужно. Вы больше не обязаны заботиться о том, какой файл
содержит какую функцию. Ctags - отличный пример применения мощи UNIX в
полном объеме.
Имея такие инструментальные средства, вам не нужно изобретать ко-
лесо, так как вы можете легко находить и выбирать те средства, которые
необходимы вам в конкретном приложении. Вы уже написали программу уп-
равления терминалом Trantor TR-101? Примените ctags и найдите ее. Более
того, самодокументируемый напечатанный файл и документация о функциях,
полученная с помощью этих командных файлов, дают другим программистам
хороший старт в понимании того, что вы сделали. Это даже может слегка
произвести впечатление на вашего начальника.
Каким в общих чертах будет наш подход к созданию таких командных
файлов? У нас есть некоторые потенциальные преимущества в применении
такого вида доступа в системе UNIX. Прежде всего, исходные файлы не от-
личаются от других текстовых файлов, поэтому мы можем использовать все
имеющиеся в UNIX средства поиска и распознавания шаблонов (sed, awk и
т.д.), чтобы находить символьные строки. Во-вторых, мы освоили технику
обхода файловых деревьев и работы с отобранными типами файлов, описан-
ную в предыдущих главах. Наш подход состоит в объединении этих средств
таким образом, чтобы они обеспечивали доступ к структурированной доку-
ментации, содержащейся в программных файлах.
4.2. Извлечение документирующих заголовков
4.2.1. stripc - из файла на языке Си

ИМЯ: stripc

stripc Извлекает документирующий заголовок
из исходного файла на языке Си.
НАЗНАЧЕНИЕ
Печатает первый блок строк комментария в файле с исходным кодом на
Си так, чтобы вы могли быстро идентифицировать назначение программы на
Си.
ФОРМАТ
stripc файл [...]
ПРИМЕР ВЫЗОВА
stripc prog*.c " header
Извлекает начальные блоки комментариев из всех файлов и помещает в
один файл с именем header.
ИСХОДНЫЙ КОД ДЛЯ stripc
1 :
2 # @(#) stripc v1.0 Strip comment header Author: Russ Sage
4 if [ "$#" -eq "0" ]
5 then echo "stripc: arg count error" "&2
6 echo "usage: stripc file [...]" "&2
7 exit 1
8 fi
10 for FILE in $@
11 do
12 if [ ! -s $FILE ]
13 then echo "file \"$FILE\" does not exist" "&2
14 continue
15 fi
17 awk '/^\/\*/, /^ \*\// { if ($0 != " */")
18 print
19 else {print;exit}
20 }' $FILE
21 echo "^L"
22 done
(Перед тем как вводить этот исходный код, обратите внимание, что в
строке 21 должен быть действительно символ control- L, введенный между
двумя кавычками, по причинам, рассмотренным ниже.)
ПЕРЕМЕННАЯ СРЕДЫ
FILE Хранит имя файла, полученное из командной строки.
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН stripc?
В больших проектах по разработке программного обеспечения требу-
ется обычно много времени для работы с документацией. Имеются программ-
ные файлы для документирования, функциональные спецификации для написа-
ния программ и, наконец, руководства и справочные карты, глоссарии,
указатели и т.д. Настоящий программный код должен иметь свою собствен-
ную встроенную документацию, иначе управление этим кодом становится
очень трудным.
Чтобы избежать путаницы, нужно создать модель документации, а за-
тем сделать ее стандартом, которому должны следовать все программисты.
Даже если эта модель не будет абсолютно идеальной, ее наличие является
первым шагом по созданию среды, которой вы можете управлять.
Следующие два инструментальные средства, которые мы предлагаем,
следуют модели документации, описанной в дальнейшем тексте. Эта модель
последовательна и понятна, ее можно дополнить или изменить по вашему
усмотрению.
ЧТО ДЕЛАЕТ stripc?
Stripc печатает только первый заголовочный блок комментариев из
начала исходного файла на языке Си. Желательно, чтобы этот блок содер-
жал всю важную информацию о файле: его официальное имя, для чего он
предназначен, кто его создал, когда он был создан и т.д. Внутри файла
может размещаться одна или несколько функций или даже главная програм-
ма. Эта модель предполагает, что весь ваш код содержит очень мало глав-
ных программ и много независимых модулей.
Рассмотрим на модельном исходном файле, какого рода информацию мы
должны извлечь из исходных файлов.
/*
* Это документирующий заголовок для файла
* с исходным кодом на языке Си.
* Он поясняет, что содержится в файле (программы, функции,
* библиотеки и т.д.) и идентифицирует проект.
*
*/ Это отметка конца заголовочного комментария.
^L Инструменты извлечения применяют control-L как разделитель.
/* Это документирующий заголовок для главной части программы.
* Главная пометка должна объяснять, что это за программа
* и что она делает. Здесь могут быть также указаны автор,
* дата и история изменений.
*/
main()
{
/* Здесь находится главная Си-программа */
}
^L
/* Это документирующий заголовок для определенной функции,
* которая за ним следует. Документируется последователь-
* ность вызова, вход и выход и общее назначение этой
* функции.
*/
func(arg1,arg2)
int arg1;
char arg2;
{
/* Текст функции находится здесь */
}
^L
/* Аналогично, этот блок комментариев документирует
* следующую функцию. Наличие документации вместе с кодом
* сокращает объем накладных расходов при чтении и
* изменении кода.
*/
func(arg1,arg2)
int arg1, arg2;
{
/* Текст функции находится здесь */
}
Как указывалось ранее, функция main не обязательна и, вероятно,
встречается только в одном или двух файлах, в зависимости от вида прог-
рамм, которые вы пишете. Один файл может иметь столько функций, сколько
вы хотите, но рекомендуемое максимальное число - от одной до трех, в
зависимости от того, как эти функции взаимосвязаны. В каждом файле
имейте дело только с одной программируемой идеей и ее реализацией.
При изучении этой модели вы видите, что обеспечивается три уровня
документации. Заголовок в начале файла извлекается с помощью stripc.
Этот заголовок относится ко всему файлу в целом. Заголовок в начале
главной программы относится ко всей программе и поддерживается с по-
мощью stripf. Заголовок для каждой функции относится к этой функции.
Эти заголовки обслуживаются командным файлом stripf, который обсужда-
ется ниже.
Отметим, что между функциями имеется прогон формата (символ
control-L кода ASCII). В предыдущем листинге мы указали эту комбинацию
клавиш с помощью символа ^L, чтобы наши текстовые процессоры не произ-
водили лишних страниц при форматировании рукописи данной книги. Вам
нужно в каждом случае действительно вводить control-L вместо ^L при
размещении комментариев в ваших файлах и при вводе исходного кода дан-
ного и последующих командных файлов. Символ прогона формата использу-
ется в модели заголовка для отметки верхней границы первой функции в
файле и для прогона страниц на печатающем устройстве при чистовой
распечатке, чтобы каждая функция появлялась на новой странице.
В начале каждой функции (перед main или перед именем функции) дол-
жен существовать документирующий заголовок. Этот заголовок обычно отра-
жает наиболее недавние изменения в этом модуле, и его можно считать бо-
лее достоверным, чем заголовок документа, который был напечатан
несколько недель или даже месяцев назад.
Входом для stripc является последовательность имен файлов с исход-
ным кодом. Для каждого файла в командной строке проверяется, существует
ли он и имеет ли размер больше, чем ноль байт. Если он не удовлетворяет
этим критериям, то печатается сообщение об ошибке и проверяется следую-
щий файл. Каждый файл читается с первого байта, и в нем ищется символь-
ная строка начала комментария (/*). Когда она найдена, информация до
символьной строки конца комментария (*/) построчно выводится в stdout.
Если правые символы не найдены, ничего не печатается, но сообщение об
ошибке не выводится, чтобы не испортить выводную информацию. После того
как каждый файл обработан, в конце печатается прогон формата, который
разбивает выводную информацию на страницы-разделы. Это применяется в
основном, когда документирующие заголовки очень длинные и нуждаются в
визуальной разбивке.
Отметим, что "извлечение" ("strip") здесь и в следующих двух ути-
литах означает не УДАЛЕНИЕ, а копирование соответствующей информации.
Никаких изменений во входных файлах не делается.
Когда все файлы в командной строке обработаны, командный файл за-
вершается.
ПРИМЕРЫ
1. $ stripc test?.c " test.filehdrs
Извлекает блок файловых комментариев из всех файлов, соответствую-
щих строке "test", за которой следуют один произвольный символ, а затем
.c. Сюда подходят test1, testA, testb и т.д. Все блоки комментариев по-
мещаются в файл test.filehdrs в таком порядке, как они обрабатывались
бы в цикле for.
2. $ for DIR in src1 src2 src3
" do
" stripc $DIR/*.c " /tmp/$DIR.hdrs
" done
Этот цикл проходит каждый из трех каталогов src1, src2 и src3. В
каждом из них имеются исходные файлы, из которых нужно извлечь докумен-
тирующие заголовки. Каждый раз берется один каталог, и stripc вызыва-
ется для всех исходных файлов на языке Си из данного каталога. Выход,
т.е. все документирующие заголовки из файлов в этом каталоге, помеща-
ется в файл в каталоге /tmp. Файлы в /tmp имеют имена /tmp/src1.hdrs,
/tmp/src2.hdrs и /tmp/src3.hdrs. Отметим, что число файлов, передавае-
мых stripc, имеет ограничение в 255 символов. После этого командная
строка переполняется и выполнение командного файла аварийно заверша-
ется.
ПОЯСНЕНИЯ
Строки 4-8 делают проверку на ошибки. Если число параметров в ко-
мандной строке нулевое, возникает ошибка. При вызове stripc должно быть
хотя бы одно имя файла.
Цикл for в строках 10-22 пробегает по каждому имени файла из
списка позиционных параметров в командной строке.
Первым делом, в строках 12-15 проверяется существование файла.
Если файл не существует, выдается соответствующее сообщение об ошибке и
цикл продолжается со следующим файлом.
Строки 17-20 - это цикл awk, который делает всю важную работу.
Входным для awk является имя файла, которое сейчас обрабатывает цикл
for.
Если вы не очень знакомы с программой awk, сообщим, что она вызы-
вается так:
awk программа имя-файла
где программа состоит из последовательности предложений, имеющих
вид:
шаблон { действие }
Указанное действие применяется к тексту, который соответствует
шаблону. Для того чтобы утилита awk работала корректно, вы должны зак-
лючить всю программу в одинарные кавычки.
В stripc для указания начала комментария (/*) и конца комментария
(*/) используются соответственно шаблоны
/^\/\*/ и /^ \*\//
Для интерпретации этих обозначений нужно вспомнить регулярные вы-
ражения ed, sed и grep. Регулярные выражения (РВ) должны быть ограниче-
ны (символом /). awk воспринимает два выражения, разделенные запятыми,
как начальный и конечный шаблоны для квалификации вводных строк.
В данном примере начальное выражение означает "от начала строки
(^), вслед за действительным символом косой черты (который должен быть
экранирован, чтобы убрать его специальное значение, т.е. \/) и действи-
тельной звездочкой (\*), использовать все строки, обнаруженные вплоть
до конечного выражения". Этим выбирается только первое вхождение со-
поставляемого шаблона (т.е. первого блока комментариев), так как прог-
рамма awk заканчивает работу при обнаружении завершающей строки.
(Остальные блоки комментариев выбираются с помощью stripf вместе с име-
нем функции и аргументами.)
Для каждой строки, которая соответствует набору выражений от на-
чального до конечного, выполняются предложения, указанные в "действии".
Строки 17, 18 и 19 содержат предложение if-then= else, которое выполня-
ет всю работу. Если $0 (что является всей строкой) НЕ равно пробелу и
концу комментария (*/), печатается вся строка. Формируя таким образом
предложение if, мы печатаем первую строку и все последующие строки, по-
ка не доберемся до конца комментария.
Когда обнаружена последняя строка, результатом проверки является
значение "ложь", поэтому выполняется else-часть. Поскольку мы знаем,
что это последняя строка, мы печатаем ее для завершенности блока ком-
ментария, а затем выходим из awk. Отметим, что благодаря вложению этих
двух команд вместе в фигурные скобки ({}), они рассматриваются как одно
составное предложение.
После завершения awk производится эхо-отображение прогона формата
на экран и берется следующий файл. Так продолжается до тех пор, пока
все файлы в командной строке не будут обработаны.
4.2.2. stripf - из Си-функции

ИМЯ: stripf

stripf Извлекает документирующий заголовок
Си-функции.
ФУНКЦИЯ
Извлекает и печатает комментирующий заголовок, имя функции с пара-
метрами вызова и объявление типов параметров для всех функций в исход-
ном файле на Си.
ФОРМАТ
stripf file [...]
ПРИМЕР ВЫЗОВА
stripf lib1.c
Извлекает документирующие заголовки для всех функций в
файле lib1.c.
ИСХОДНЫЙ КОД ДЛЯ stripf
1 :
2 # @(#) stripf v1.0 Strip function header Author: Russ Sage
4 for FILE in $@
5 do
6 sed -n -e '
7 /^L$/ {
8 s/^L$/.bp/p
9 : loop
10 n
11 /^{/b exit
12 p
13 b loop
14 : exit
15 i\
16 {}
17 b
18 }' $FILE
19 done
ПЕРЕМЕННАЯ СРЕДЫ
FILE Хранит имя файла для каждого файла
из командной строки.
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН stripf?
Предположим, что наш код на языке Си соответствует модели докумен-
тации, представленной ранее при описании stripc. Тогда нам нужен способ
поддержания изменений в документации по ходу изменений кода. Мы видели,
что при хранении документации в исходных файлах более вероятно, что она
будет изменена, когда изменится код. Проблема возникает, когда нам нуж-
на твердая копия документации, которая находится внутри исходного кода.
Как нам получить ее из файлов?
ЧТО ДЕЛАЕТ stripf?
Командный файл stripf решает эту проблему. Он просматривает весь
файл и печатает всю документацию для каждой ФУНКЦИИ, которая размещена
в этом файле (включая "main", если она есть).
Входом для stripf являются имена файлов, переданные в командной
строке. Stripf обрабатывает файлы по очереди и помещает выход в stdout.
Этот выход можно перенаправить, но вход должен быть в командной строке.
К выходу применяются дополнительные модификации, чтобы сформиро-
вать данные для среды утилиты nroff, поэтому выводные файлы можно фор-
матировать с помощью этой утилиты. Все прогоны формата заменяются на
команду .bp, принятую в nroff для начала страницы. Эта команда nroff
прогоняет страницу и увеличивает на единицу счетчик страниц. Возможно,
вы не хотите менять нажатие клавиш control-L на это значение, но вы
всегда можете указать в командном файле такие действия, какие вам нуж-
ны.
ПРИМЕРЫ
1. $ stripf module1.c | grep ""^\.bp$" | wc -l
Печатает число модулей-функций, содержащихся в файле module1.c,
путем поиска каждого появления команд новой страницы программы nroff и
их подсчета. Мы знаем, что одна из таких команд является выходом для
каждой обнаруженной функции. Данную строку можно вложить в предложение
echo, которое говорит "имеется X модулей в файле $FILE".
2. $ for FILE in *.c ../*.c $HOME/src/*.c
" do
" stripf $FILE
" done "" /tmp/func.hdrs
Данный цикл for распространяется на все файлы в текущем каталоге,
которые оканчиваются на .c, все файлы в родительском каталоге с таким
же суффиксом и все файлы в подкаталоге src моего home-каталога с тем же
суффиксом. Из каждого файла извлекается документация о функциях и нап-
равляется в стандартный вывод. Весь цикл перенаправлен с помощью "",
поэтому выход каждого вызова stripf ДОБАВЛЯЕТСЯ к файлу в /tmp.
ПОЯСНЕНИЯ
Вся программа - это один большой цикл for в строках 4-19. Этот
цикл присваивает переменной FILE каждое имя, имеющееся в командной
строке. Данный командный файл не имеет опций и обработки ошибок.
Команда sed системы UNIX вызывается для каждого имени файла. Прог-
рамма sed читает весь вход и выводит измененный текст в стандартный вы-
вод.
Опция -n используется в sed для подавления всего вывода, в проти-
воположность действию по умолчанию, когда все печатается. Мы используем
этот флаг по той причине, что мы хотим указать программе sed, когда пе-
чатать выход. Опция -e применяется, чтобы сообщить программе sed, что
следующая последовательность текста между одинарными кавычками является
выражением, которое нужно вычислить.
Напомним, что sed - потоковый редактор, который читает одну стро-
ку, сверяет ее с выражениями, затем читает следующую строку и делает
все сначала. Первое, что мы ищем - символ control-L, стоящий в строке
самостоятельно. Если мы не находим его, проверяется следующая строка и
так далее, пока не будет обнаружен control-L. (Еще раз напомним, что
вместо обозначения ^L в коде должен быть введен настоящий control-L.)
Когда обнаружен control-L, он активизирует все выражение, заклю-
ченное в фигурные скобки. Первым действием является подстановка команды
начала страницы программы nroff, как описано ранее. Эта подстановка пе-
чатается, что является самым первым выводом. В строке 9 объявлена метка
"loop". Это не приводит ни к каким действиям, но устанавливает точку
перехода, которая впоследствии используется. (Управляющие структуры
программы sed довольно примитивны, но они позволяют описать выполняемую
работу.)
Строка 8 использует команду n программы sed, чтобы вызвать чтение
следующей строки. Мы разобрались с первой строкой - строкой, которая
содержит control-L - так что мы можем ее отбросить. В случае выполнения
цикла мы видим, что sed продвигается по нашему требованию, но не прод-
вигается сам.
Вспомним модель документации, рассмотренную ранее. Эта модель
включает документирующий заголовок для файла в целом, выполненный в
обычном стиле языка Си. Модель завершается символом control-L. Этот
первый блок обрабатывается с помощью stripc, как описано ранее. Мы не
хотим использовать его здесь при работе со stripf. Поэтому мы сейчас
должны спозиционироваться после файлового документирующего заголовка.
Вслед за символом control-L имеется еще один набор из одной или
более строк комментария языка Си, которые описывают функцию, следующую
за ними. Далее идет само имя функции, объявление параметров и открываю-
щий символ самой функции, которым является левая фигурная скобка (}).
Строка 11 ищет эту фигурную скобку. Если она найдена, выполнение
переходит на метку exit (строка 14). Мы можем полагать, что мы все сде-
лали, если найдена левая фигурная скобка, так как этот символ должен
появляться только в начале функциимодуля. Когда мы находим фигурную
скобку, мы уже напечатали к этому моменту всю комментирующую информацию
и заголовок функции посредством строки 12, которую мы сейчас опишем. А
что если фигурная скобка появляется в поле комментария? Нет проблем,
поскольку поиск фигурной скобки привязан к началу строки с помощью сим-
вола ^. Он производит выражение, означающее "от первого символа в стро-
ке". Мы только тогда сопоставляем фигурную скобку этому выражению, ког-
да она встречается в качестве самого первого символа в строке.
Затем строка 12 предполагает, что мы еще не обнаружили фигурную
скобку и поэтому мы должны напечатать строку. Оператор p печатает теку-
щую строку, которую обрабатывает sed. Этот вывод направляется на экран.
Строка 13 - безусловный переход на метку loop. Отметим, что этот
переход только изменил процесс выполнения и не привел к чтению еще од-
ной вводной записи. С этим мы должны быть осторожны при управлении про-
цессом выполнения в программе sed. Мы только что напечатали текущую за-
пись, поэтому теперь мы должны отбросить ее и получить следующую за-
пись, что означает возврат в строку 9. Этот цикл печати и чтения следу-
ющей записи продолжается до обнаружения фигурной скобки, которая пере-
водит выполнение на метку exit.
Строка 14 - это метка exit. Когда мы попадаем на нее, мы знаем,
что был обнаружен control-L, напечатан комментирующий заголовок, напе-
чатаны имя функции и объявления ее параметров и найдена фигурная скоб-
ка. Заметим, что фигурная скобка еще не напечатана. Когда мы находим
ее, мы только делаем ветвление.
Строка 15 завершает вывод, вставляя некоторый текст в выводной по-
ток. Мы не можем сказать "печатать это в буквенном виде", поэтому про-
исходит движение вправо по тексту, как по команде echo. Мы должны сыг-
рать на правилах, установленных программой sed. Любой вывод должен быть
порожден обычными командами в стиле редактора ed. Вставка текста с по-
мощью команды "i" делает нам это. Отметим, что мы также вставляем сим-
вол возврата каретки (или перевода строки, в зависимости от вашей осве-
домленности). Он может быть определен символом обратной косой черты
(\). Обратная косая черта убирает специальное значение символов и при
использовании в конце строки, как здесь, означает, что специальный сим-
вол, вставленный в выражение, является возвратом каретки. Вдобавок к
возврату каретки, мы вставляем пару фигурных скобок. Это обозначает
объявление начала-конца функции, которую мы обрабатываем. Поскольку мы
вставляем текст, мы не должны говорить программе sed, что его нужно пе-
чатать.
Строка 17 - безусловный переход на себя, указывающий программе sed
переход на вершину всего обрабатываемого выражения. Когда это происхо-
дит, мы завершаем поиск еще одного control-L и начинаем весь процесс
снова. Таким образом, мы можем обработать все функции из одного файла
независимо от того, сколько их там.
Строка 18 является концом sed-выражения и содержит также имя фай-
ла, которое должно быть передано программе sed. Это является частью
обычного синтаксиса, принятого в sed, но выглядит несколько неуместным,
так как не выделено специальным отступом. Когда все файлы обработаны,
завершается внешний цикл и заканчивается работа командного файла.
4.2.3. strips - из командного файла Shell

ИМЯ: strips

strips Извлекает документирующий заголовок
командного процессора.
ФУНКЦИЯ
Печатает начальные строки комментария к командному файлу командно-
го процессора, что выражено буквой "s" в имени. Первая строка игнориру-
ется для совместимости с командным процессором языка Си.
ФОРМАТ
strips файл [...]
ПРИМЕР ВЫЗОВА
strips *.sh
Извлекает комментарии из всех командных файлов в текущем каталоге.
ИСХОДНЫЙ КОД ДЛЯ strips
1 :
2 # @(#) strips v1.0 Strip shell comment header
Author: Russ Sage
4 for FILE in $@
5 do
6 cat $FILE | (read LINE; echo $LINE
7 while read LINE
8 do
9 if [ "`echo $LINE|cut -c1`" = "#" ]
10 then echo "$LINE"
11 else exit
12 fi
13 done)
14 done
ПЕРЕМЕННЫЕ СРЕДЫ
FILE Хранит каждое имя файла, полученное из
командной строки.
LINE Хранит каждую строку вводного текста,
полученную из читаемого файла.
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН strips?
Так же как нам нужны средства обработки документации для файлов с
исходным кодом на Си, нам нужны и аналогичные средства для командных
файлов командного процессора. Разница между извлечением комментариев из
кода на языке Си и их извлечением из кода командных файлов командного
процессора - в способе разграничения комментариев в исходном файле.
Исходный файл на Си использует пару /* */, а командные файлы командного
процессора применяют #, чтобы выделить остаток строки как комментарий.
Обработка документации облегчается, когда командные файлы следуют
некоторой форме стандартизованной документации. В действительности нет
формального стандарта, но наиболее близкий стандарт приходит со страниц
руководств по самой системе UNIX. Стандартными полями являются имя ко-
мандного файла, способ его вызова, что он делает и, возможно, некоторые
пометки о том, как он работает, ссылки на другие места для поиска ин-
формации и сведения о том, какие файлы использует данный командный
файл. Пример формата выглядит так:
:
# ИМЯ
# strips Извлекает поля shell-комментариев
#
# ФОРМАТ ВЫЗОВА
# strips файл [...]
#
# АВТОР
# Russ Sage mm/dd/yy
#
# ОПИСАНИЕ
# Данный командный файл извлекает комментирующие
# заголовки из командных файлов интерпретатора shell.
#
# СМ. ТАКЖЕ
# sh(1), cat(1)
Отметим, что в первой строке имеется оператор :, который является
для командного процессора нулевым оператором. Он ничего не делает, но
всегда возвращает успешный статус выхода. Он находится в отдельной
строке, поскольку это обозначает командный файл для Bourne shell. Если
вы запускаете /bin/csh вместо /bin/sh, командные файлы могут вызвать
путаницу. C Shell ищет символ # в первой колонке первой строки. Если он
там есть, командный процессор считает этот файл командным файлом ин-
терпретатора csh. Чтобы указать интерпретатору csh, что командный файл
предназначен для интерпретатора Bourne shell, мы можем оставить эту
строку пустой (что будет не слишком хорошо печататься и подлежит удале-
нию) или поместить оператор, который сообщает интерпретатору C Shell,
что это командный файл для Bourne shell, но ничего не делает под управ-
лением Bourne shell.
ЧТО ДЕЛАЕТ strips?
Strips читает командный файл и печатает все комментарии с начала
файла, которые являются непрерывными (т.е. печатает, пока не достигнет
строки, не являющейся комментарием). Первая строка игнорируется, но пе-
чатается. Когда строка не имеет символа # в первой символьной позиции,
strips прекращает чтение этого файла.
Командный файл должен иметь структуру комментария, аналогичную
структуре, показанной в нашем предыдущем примере. Символы # должны быть
в первой позиции, и каждая строка должна иметь символ #. Если у вас
есть пустая строка где-нибудь в начальном блоке комментариев, strips
печатает только первую часть блока.
ПРИМЕРЫ
1. $ strips `kind /bin /usr/bin`
Блоки комментариев извлекаются из текстовых файлов, размещенных в
каталогах /bin и /usr/bin.
2. $ find / -name "*.sh" -print | while read FILE
" do
" strips $FILE " /tmp/doc/$FILE
" done
Find порождает список всех имен файлов, который попадает в strips.
Выход strips направляется в каталог документации в /tmp. Окончательный
выход попадает в файл с точно таким же именем, как исходный файл, толь-
ко выход помещается в /tmp, поэтому никаких случайных удалений не про-
исходит.
ПОЯСНЕНИЯ
Строки 4 и 14 окаймляют внешний цикл, который подает имена файлов
данному командному файлу. Обработки или проверки ошибок нет. Пока в ко-
мандной строке есть файлы, цикл продолжается. Вы можете, конечно, про-
верить наличие аргументов, правильность и существование файлов. Для
этого, мы думаем, вы видели достаточно примеров проверки ошибок, чтобы
добавить их, куда вам нужно. Поэтому мы иногда опускаем такие фрагменты
в нашем коде, чтобы сэкономить место и выделить главную функцию.
Строка 6 применяет команду cat к файлу, который сейчас обрабатыва-
ется. Выход направляется в конвейер, чтобы его прочитал другой shell.
Новый shell получает длинную командную строку (обозначенную скобками в
строках 6 и 13). Первая команда read читает самую первую строку.
Поскольку мы не собираемся проверять эту строку, мы отображаем ее, а
затем опускаемся в цикл последовательного чтения. Предполагается, что
первая строка начинается с двоеточия, но если она начинается с символа
#, она все равно печатается, так что вы не будете терять текст.
Строки 7-13 являются внутренним циклом while, читающим строки
текста со стандартного ввода, который был выходом команды cat. Когда
текст заканчивается, прекращается и цикл while.
Строки 9-12 - это строки принятия решения. Сначала мы отображаем
текущую вводную строку и передаем ее по конвейеру команде cut. Выреза-
ется первый символ, затем команда сравнения проверяет, совпадает ли он
с символом комментария. Если да, строка отображается на стандартный вы-
вод. Если это не символ комментария, то нужно достичь конца блока ком-
ментариев, поэтому происходит выход из внутреннего цикла. Управление
возвращается во внешний цикл (for), который стартует и берет следующее
имя файла. Когда все файлы обработаны, strips завершается.
4.3. ctags - создание файла признаков исходного кода проекта

ИМЯ: ctags

ctags Делает файл признаков исходного кода для
простоты доступа с помощью утилиты vi.
ФОРМАТ
ctags [файл ...]
ПРИМЕР ВЫЗОВА
ctags proj*.c
Делает файл признаков для всего исходного кода проекта.
ИСХОДНЫЙ КОД ДЛЯ ctags
1 :
2 # @(#) ctags v1.0 Create a C source code tag file
Author: Russ Sage
4 awk -F'(' '/^[a-zA-Z_][a-zA-Z0-9_]*\(/ {
5 printf ("%s\t%s\t/^%s$/\n", $1, FILENAME, $0) }'
$@ | sort -u +0 -1
ПЕРЕМЕННАЯ СРЕДЫ
FILENAME awk Переменная, содержащая имя файла.
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН ctags?
UNIX создан как среда для разработки программного обеспечения. Она
поддерживает и поощряет модульность исходного кода программы. Модуль-
ность - это концепция разбиения проекта на отдельные файлы, превращения
идей в подпрограммы и компиляции отдельных файлов с исходным кодом в
перемещаемые модули для последующей их сборки в исполняемый модуль.
Такая философия разработки программного обеспечения может, однако,
породить некоторые проблемы. Главная проблема - попытка получить неко-
торого рода сцепку из всех маленьких кусков головоломки. Делаются вызо-
вы подпрограмм, которые находятся в других файлах, возможно даже в дру-
гих каталогах. Нужен инструмент, позволяющий нам, людям, посмотреть на
программное обеспечение человеческим взглядом, т.е. содержательно, а не
с точки зрения физического размещения. Этот подход чем-то аналогичен
чтению книги в сравнении с чтением компьютерной распечатки. Распечатка
заставляет вас делать последовательный просмотр, а книга допускает пря-
мой доступ (и обычно предоставляет оглавление и предметный указатель
для поиска специфических пунктов).
Ctags преодолевает этот разрыв, создавая файл специального форма-
та, который распознают редакторы vi и ex. Этот файл содержит "призна-
ки", которые могут быть использованы при работе с редактором для
обеспечения автоматического доступа к любой нужной функции, не требую-
щего от вас знаний о том, в каком файле находится функция.
Фактически, ctags предоставляет вам предметный указатель для груп-
пы файлов с исходным кодом на языке Си. Когда вы объединяете его с ре-
дактором, вы можете быстро найти любую функцию по известному вам имени
и посмотреть тело функции. Это значит также, что вы можете легко копи-
ровать и вставлять функции в любой исходный файл, с которым вы сейчас
работаете.
Если редактор не имел возможности работы с признаками или мы не
построили инструментальное средство, использующее такое преимущество,
то мы должны запускать grep для имени функции на наборе исходных файлов
на Си (в надежде, что у нас есть подходящие файлы!), отмечать, какой
файл имеет требуемую функцию, входить в этот файл редактором (вручную
вводя все символы имени файла), а затем набирать символы шаблона по-
иска. Это большая работа, которая может занять много времени. Благодаря
использованию возможности работы с признаками, для файла признаков, из-
влеченных из исходного кода, вся эта ручная работа сокращается.
Это сочетание возможностей иллюстрирует то, чему не часто прида-
ется значение: владельцы UNIX всегда настороженно относятся к возмож-
ности использовать преимущества многочисленных средств, уже имеющихся в
таких программах, как vi или ex. Зачастую от 90 до 95 процентов необхо-
димых вам возможностей уже имеются, ожидая относительно простого ко-
мандного файла интерпретатора shell, связывающего их вместе в мощный
новый инструмент.
Ctags уже существует в виде исполняемого модуля в системе Berkely
(BSD) и в нынешней AT&T System V. Он происходит из системы Berkely, но
поддерживается теперь в System V. Это иллюстрация взаимодействия между
этими двумя источниками в мире UNIX, поскольку они взаимствуют полезные
идеи друг у друга. Данное конкретное воплощение ctags является команд-
ным файлом утилиты awk, имитирующим исполняемую программу из системы
Berkely, а это значит, что пользователи систем XENIX и предыдущих
версий AT&T могут теперь извлечь пользу от применения ctags. Еще одно
преимущество версии в виде командного файла в том, что его можно легко
модифицировать, чтобы обрабатывать другие особенности языка Си. Такое
вы не можете делать с исполняемым модулем, если только у вас нет доро-
гостоящей лицензии на исходный код.
ЧТО ДЕЛАЕТ ctags?
Ctags просматривает файлы с исходным кодом на Си, переданные в ко-
мандной строке, и печатает список имен функций в каждом исходном файле.
Имена функций имеют специальный синтаксис и должны быть именно в таком
формате, иначе awk не распознает их как таковые. Эти правила заключа-
ются в том, что имя функции должно находиться в начале строки, состоять
из разрешенных символов и за ним должна следовать левая скобка. Пробелы
в имени функции не допускаются. Вот пример модуля программы на Си, по-
даваемого на рассмотрение командному файлу ctags:
main()
{
}
func1(arg1,arg2)
int arg1,arg2;
{
}
func2(arg1,arg2)int arg1,arg2;
{
}
Результат работы ctags направляется в стандартный вывод (на эк-
ран), поэтому он должен быть перенаправлен, чтобы попасть в файл. Вхо-
дом для ctags является любое число имен файлов. Напомним, что если на
входе имеется несколько файлов, то выход представляет собой один непре-
рывный поток данных, попадающий в один файл. Если вам нужен выводной
файл для каждого входного файла, то для управления ctags можно приме-
нить такой командный файл с циклом:
for F in *.c
do
ctags $F " $F.tags
done
Выход ctags состоит из трех полей в таком формате:
признак имя_файла шаблон_поиска
Реальный выход для примера программы на Си, приведенного выше, был бы
таким:
main /usr/russ/src/program.c /^main()$/
func1 /usr/russ/src/program.c /^func1(arg1,arg2)$/
func2 /usr/russ/src/program.c /^func2(arg1,arg2)$/
Первое поле является именем признака (которое совпадает с именем
функции). Второе поле - маршрутное имя файла, содержащего данную функ-
цию. Третье поле - шаблон поиска, используемый признаковыми средствами
редактора для доступа к функции внутри файла (более подробно об этом -
позже).
Предположим, вы можете сгенерировать правильный файл признаков.
Как согласовать файл признаков с редакторами таким образом, чтобы вы
могли найти интересующую вас функцию? Редактор vi предоставляет много
путей для этого. Первый способ - поместить имя используемого файла
признаков в файл .exrc. (Файл .exrc является аналогом файла .profile
для редактора ex и работает также с редактором vi, что не удивительно,
так как vi построен na ex. Поскольку vi - наиболее популярный редактор
системы UNIX, мы применяем его здесь для наших примеров.) Вы можете
иметь файл .exrc, который выглядит примерно так:
set tags=/usr/russ/mytags
Впоследствии, когда вы обращаетесь к некоторому признаку, исполь-
зуется данный файл признаков. Другой способ - установить файл признаков
после того, как вы вошли в редактор. Чтобы посмотреть, каким является
ваш файл признаков по умолчанию, введите, находясь в vi, следующее:
:set tags
Эта команда печатает файл признаков, о котором она знает. Для из-
менения определенного в настоящий момент файла признаков, используйте
синтаксис, который был в примере файла .exrc:
:set tags=/usr/russ/mytags
Теперь, когда редактор знает, в каком файле искать признаки, к ко-
торым вы обращаетесь, давайте рассмотрим, как обращаться к признаку
(т.е. к имени функции). Первый способ - объявить его в командной строке
при вызове редактора. Например:
$ vi -t tag
Если вы уже находитесь в редакторе vi, можете применить такую ко-
манду для поиска признака:
:ta tag
Двоеточие означает, что мы направляемся в ex, чтобы выполнить этот
поиск. Мы просим ex найти указанную строку как признак, который разме-
щается в текущем файле признаков. Когда этот признак найден в файле
признаков, редактор vi редактирует файл с соответствующим именем, кото-
рое он берет из поля 2. Это аналогично команде ":e имя_файла". Когда
новый файл внесен в буфер редактора, последнее поле файла признаков
используется в качестве строки шаблона поиска. Синтаксис точно такой
же, как если бы вы набирали его вручную. Курсор перемещается в позицию
в файле, которая соответствует строке поиска, при этом вы попадаете на
интересующую вас функцию.
ВЗАИМОСВЯЗЬ МЕЖДУ ex И vi
Несколько отклоняясь от темы, рассмотрим два файла: /bin/ ex и
/bin/vi. Небольшое исследование обнаруживает, что на самом деле это
один и тот же файл. Мы можем проверить это, посмотрев на их индексные
описатели файлов. Введите такую команду:
$ ls -li `path ex vi`
Выход показывает, что два числа в первой колонке одинаковы.
510 -rwx--x--t 5 bin bin 121412 Sep 19 1985 /bin/ex
510 -rwx--x--t 5 bin bin 121412 Sep 19 1985 /bin/vi
Это число и есть индексный описатель файла (inode). Поскольку оба
файла являются одним и тем же, вызов любого из них запускает один и тот
же исполняемый модуль. Каким образом он знает, как вы его вызвали?
Программа смотрит на строку argv[0] и видит, какое имя вы использовали
для вызова файла. Затем редактор устанавливает свой интерфейс в соот-
ветствии с тем, как вы его вызвали.
Обратите внимание, что эта программа имеет пять связей. Как нам
найти все другие имена, которыми можно вызвать vi и ex? Мы можем
использовать команду системы UNIX ncheck. Эта команда воспринимает ин-
дексный описатель файла и печатает все файлы, имеющие такой описатель
файла . Примеры таких команд:
$ ncheck -i 510 /dev/root
$ ncheck -i 510
Первый синтаксис указывает команде ncheck искать файлы с inode,
равным 510, только в корневой файловой системе. Ncheck ограничена по-
иском в одной файловой системе. Это подкрепляет тот факт, что файлы не
могут быть привязаны к различным файловым системам, поскольку каждая
файловая система начинается с inode 2 и последовательно наращивается.
Каждая файловая система имеет inode 510, который уникален для каждой
файловой системы. Выход предыдущей команды выглядит так:
dev/root:
510 /bin/edit
510 /bin/ex
510 /bin/vedit
510 /bin/vi
510 /bin/view
Если файловая система не указана, как во втором примере, выполня-
ется поиск по всем файловым системам, смонтированным в настоящее время.
Это не слишком хорошо для нас, поскольку пять связей vi должны нахо-
диться в одной и той же файловой системе. В противном случае файлы были
бы связаны поперек границ файловых систем. Мы уже можем сказать, что
никаких файлов редактора нет в каталоге /usr, размещенном во втором
разделе диска от корневой файловой системы.
ПРИМЕРЫ
1. $ ctags *.c
Генерирует файл признаков для всех файлов с исходным кодом на Си в
текущем каталоге. Выход направляется на экран в отсортированном порядке
для каждого файла. Порядок файлов алфавитный, так как указано расшире-
ние файла. Конечно, в большинстве случаев вы захотите перенаправить вы-
ход ctags в файл.
2. $ ctags `Find /usr/src -name "*.c" -print`
Этот синтаксис использует командную подстановку, чтобы найти все
имена файлов, оканчивающиеся на .c и поместить их в командную строку.
Проблема в том, что если найдено слишком много файлов, командная строка
ctags может переполниться и испортить всю команду. В зависимости от
серьезности переполнения, она может испортить и весь ваш процесс shell.
3. $ find /usr/src -name "*.c" -exec ctags {} \; " tags
Находит все исходные файлы на Си в сегменте дерева /usr/src. Для
каждого подходящего файла запускает программу ctags на этом файле.
Использование такой формы записи предотвращает порчу вашей команды (о
которой только что шла речь), а также запускает ctags для каждого имени
файла. Весь выход помещается в файл tags для последующего использова-
ния.
4. $ find /usr/src -type f -print | sort |
" while read FILE
" do
" ctags $FILE
" done "" tags
Используя преимущество множественных каталогов, находит и сортиру-
ет все файлы из каталога /usr/src и печатает их маршрутные имена в
стандартный вывод. Затем они сортируются и поступают по конвейеру в
цикл while. Цикл while используется для обслуживания сколь угодно боль-
шого числа файлов без переполнения буферов. Выход цикла while добавля-
ется к одному файлу - tags. Этот цикл громоздкий и медленный, но он вы-
полняет свою работу.
5. $ find /usr/src -print | ctags
Это неправильный способ использования ctags. Выходом команды find
являются маршрутные имена. Ctags читает стандартный ввод, поскольку в
командной строке нет файлов. Получается, что данные, которые читает
awk, являются маршрутными именами от find, которые не имеют корректных
полей для соответствия шаблонам функций. Никакого сопоставления не про-
исходит.
Аналогичную проблему могла бы вызвать такая командная строка:
find /usr -print | wc -l
Вы могли бы интерпретировать это так: "посчитать, сколько строк
имеется во всех файлах каталога /usr". Но в действительности здесь ска-
зано: "сколько имен файлов имеется в древовидной структуре /usr". Для
подсчета общего количества строк в этих файлах нужен такой синтаксис:
find /usr -exec cat {} \; | wc -l
который гласит: "найти все файлы в /usr, распечатать каждый из
них, затем посчитать, сколько строк в них имеется". Чтобы так же посту-
пить с ctags, нужен такой синтаксис:
find /usr/src -name "*.c" -exec cat {} \; | ctags
В отличие от результата, который мы получили бы в предыдущих при-
мерах:
func1 /usr/russ/src/program.c /^func1(arg1,arg2)$/
func2 /usr/russ/src/program.c /^func2(arg1,arg2)$/
теперь выход будет выглядеть так:
func1 - /^func1(arg1,arg2)$/
func2 - /^func2(arg1,arg2)$/
ПОЯСНЕНИЯ
Символы "-" вместо имени файла появляются из-за того, что ctags
читает из стандартного ввода. Awk автоматически присваивает своей внут-
ренней переменной FILENAME значение "-", так как знает, что в командной
строке не было файлов.
Весь командный файл есть программа awk. Входом для командного фай-
ла утилиты awk является $@, что представляет все позиционные параметры.
Каждый параметр считается именем исходного файла на Си. Если никакие
файлы не передаются в командной строке, awk ищет данные в стандартном
входном потоке, но это создает некорректный выход, так как переменная
FILENAME в awk имеет по умолчанию значение "-". Поскольку awk требует
имена файлов, мы должны вызывать ctags с именами файлов, а не переда-
вать ему данные по конвейеру через стандартный ввод, как показано в
предыдущем примере.
Awk читает каждый раз одну строку из файла данных и сверяет ее с
шаблоном, пытаясь установить соответствие. Для каждой строки, соот-
ветствующей шаблону, awk выполняет заданную программу. Первое, что де-
лает ctags,- изменяет подразумеваемый разделитель полей утилиты awk с
пробела на левую скобку. Благодаря использованию этого символа в ка-
честве разделителя полей, строка определения функции разбивается на два
поля: имя функции и остаток строки.
Шаблон поиска утилиты awk соответствует синтаксису имени Си-функ-
ции. Оно может начинаться с символов a-z, A-Z или символа подчеркива-
ния. Далее в имени могут быть любые символы из набора a-z, A-Z, 0-9 и
_. Между именем и скобкой нельзя использовать пробелы. Поиск начинается
от начала строки (^), за которым следует последовательность допустимых
символов (a-z, A-Z, 0-9), а затем левая скобка.
Когда строка соответствует данному шаблону, генерируется выход с
помощью оператора printf. Первое поле - строка, представленная обозна-
чением $1. В данном случае $1 - это только имя функции, исключая левую
скобку. Печатается символ табуляции, затем следующая строка, которая
является переменной FILENAME из утилиты awk. Эта переменная должна быть
получена из командной строки, иначе awk не будет знать имя файла, в ко-
тором размещена данная функция, и файл признаков потеряет информацию,
необходимую для доступа к файлу, содержащему функцию. Печатается еще
одна табуляция, затем строка поиска. Строкой поиска является $0, что
представляет всю строку, с которой работает awk. Строке предшествует
символ ^, а за строкой следует символ $.
Выход пропускается по конвейеру через sort с той целью, чтобы все
признаки шли в отсортированном порядке. Опции сортировки указывают ути-
лите sort проверять только первое поле и печатать только одно появление
строки, если имеется несколько записей.
МОДИФИКАЦИИ ДЛЯ ctags
Теперь, когда мы знакомы с общим форматом редактируемого файла
признаков, можем ли мы применить его для других полезных целей? Мы зна-
ем, что мы можем идентифицировать регулярные структуры в программах на
Сии создать признаки, с помощью которых можно получить доступ к этим
структурам в редакторе. В программах на Си имеются не только имена
функций, но и другие интересные конструкции, например имена структур.
Их можно обслуживать с помощью модифицированной версии ctags.
Единственное, что нам нужно знать,- это официальный синтаксис структуры
данных. Структура данных, которая нас бы заинтересовала, часто имеет
такой формат:
struct name {
int val1;
char val2;
};
Все, что мы должны сделать,- это заставить awk искать все появле-
ния определения структуры. Затем мы можем построить файл признаков, в
котором признаком является имя структуры. Этот файл, видимо, будет та-
ким же, как и прежде, а строка поиска будет обнаруживать определение
структуры, а не имя функции. Фактически, комбинация утилиты awk, приз-
наков и редактора может быть использована для любого вида информации,
которую вы можете захотеть хранить в файле специального формата, напри-
мер для адресов, заметок, библиографических ссылок и т.д. Вам просто
нужно подобрать соответствующие разделители и правильно их использо-
вать.
Мы надеемся, что облегчили сопровождение ваших программ и предло-
жили вам идеи для других способов автоматической обработки документа-
ции. Вы можете без особого труда учреждать и поддерживать локальные
соглашения о документации с помощью командных файлов, аналогичных
представленным здесь. Примером проекта, за который вы можете взяться,
является согласование наших программ извлечения информации (stripf,
stripc, strips) и других программ, которые вы пишете, таким образом,
чтобы они могли читать файл-формирователь (makefile, см. Make(1)) и вы-
давать полную документацию по всем исходным файлам, участвующим в дан-
ном проекте.
* ГЛАВА 5. УПРАВЛЕНИЕ ЛИЧНОЙ ИНФОРМАЦИЕЙ I: *
УПРАВЛЕНИЕ ВРЕМЕНЕМ
И ДЕЛОПРОИЗВОДСТВОМ
УПРАВЛЕНИЕ ВРЕМЕНЕМ
at выполнение задач в указанное время
b порожденный shell фоновых задач
greet своевременное приветствие с терминала
lastlog сообщение времени последней регистрации
timelog учет и статистика сеансов работы
today печать календаря с отмеченной текущей датой
УПРАВЛЕНИЕ ДЕЛОПРОИЗВОДСТВОМ
jargon генератор технических терминов
phone база данных с телефонными номерами
office делопроизводитель
УПРАВЛЕНИЕ ЛИЧНОЙ ИНФОРМАЦИЕЙ I:
УПРАВЛЕНИЕ ВРЕМЕНЕМ
И ДЕЛОПРОИЗВОДСТВОМ
ВВЕДЕНИЕ
Мы уже многое знаем о файлах и о том, как управлять файловой
структурой. Пора рассмотреть, как мы можем использовать систему UNIX
для управления множеством задач, которые составляют наш рабочий день и
держат нас в курсе того, что делают другие пользователи. Термин "управление
личной информацией" (personal management) подразумевает, что
вы хотите создать свою собственную ПЕРСОНАЛЬНУЮ рабочую среду и
инструментальные средства. Мы предлагаем вам пакет программ, которые
вы можете приспособить к вашим требованиям. Фактически мы в этой и
следующей главе представляем четыре отдельных набора программ, каждый
из которых посвящен определенному аспекту управления личной информацией.
Средства управления временем помогают нам спланировать выполнение
задач компьютером, а также контролировать наше личное время. Управление
делопроизводством имеет дело с хранением и извлечением информации,
а также с организацией доступа к различным функциям системы UNIX
посредством простого в использовании интерфейса в виде меню.
Для каждой из этих областей деятельности мы даем ее обзор, а затем
представляем соответствующую группу средств.
УПРАВЛЕНИЕ ВРЕМЕНЕМ
Поскольку система UNIX имеет встроенные функции поддержки времени
и часы, она может следить за временем. Объединение функций поддержки
времени с возможностью автоматического запуска группы команд означает,
что мы можем настроить их так, чтобы компьютер выполнял многие наши
рутинные работы, связанные со временем. Мы также можем использовать
компьютер для отслеживания нашего собственного времени.
В данном разделе представлены инструментальные средства at, b,
greet, lastlog, timelog и today.
Командный файл at дает нам возможность сказать машине о том, что
в указанное время необходимо сделать то-то и то-то (вывести на экран
сообщение или выполнить какие-то другие команды). Задача запускается в
фоновом режиме, так что мы можем продолжать другую работу, а фоновая
задача выполнится автоматически в указанное время. Эта задача может
состоять из любых разрешенных в UNIX команд, поэтому ее возможности
очень гибкие. Мы просто предлагаем некоторые идеи, связанные с ее
использованием.
Вторым средством является командный файл b. Это обработчик фоновых
задач. Очень часто при порождении фоновых процессов мы не можем
узнать, когда они закончились. Для того, чтобы это определить, нам необходимо
вручную просмотреть таблицу процессов или найти какой-то иной
признак того, что данная работа завершена. Командный файл b запускает
задачу, управляет операциями ввода-вывода и затем сообщает нам о том,
что задача завершена.
Командный файл greet показывает, каким образом переводить внутреннее
время компьютера в более понятные пользователю категории. Он
различает три перида суток (утро, день и вечер) и реагирует на них
соответствующими сообщениями. Это довольно просто, но обеспечивает
неплохое основание для подхода к решению других проблем, связанных со
временем.
Далее мы представляем два средства, которые образуют базис системы
управления временем. При выполнении множества работ нам необходимо
подсчитать время, которое мы потратили на данный проект, чтобы мы могли
выставить нашему клиенту соответствующий счет. Командный файл
lastlog запускается автоматически, когда вы регистрируетесь в системе.
Поддерживается база данных, в которую каждый раз записывается время
вашей регистрации для последующего анализа или хранения записей.
С этим инструментальным средством соседствует командный файл
timelog. Это утилита, которая выполняет подсчет времени. Она может
следить за общим временем, затраченным на любой указанный проект. Затем
можно сгенерировать статистику, которая показывает, когда и сколько
времени вы работали над каждым проектом.
Последнее средство, относящееся ко времени - это командный файл
today. Это утилита, которая изменяет вид выходных данных команды UNIX
cal. Она печатает обычный календарь, только текущая дата выводится в
инверсном виде. Это очень наглядно. Вы можете развить этот инструмент
для того, чтобы отмечать праздники или другие особые дни.

ИМЯ: at

at - выполнить команду или файл в указанное время
НАЗНАЧЕНИЕ
Переводит любую командную строку в фоновый режим и выполняет ее в
заданное время.
ФОРМАТ ВЫЗОВА
at hr:min cmd [;cmd ...]
ПРИМЕР ВЫЗОВА
at 12:00 echo "time for lunch!"
В двенадцать часов дня выводит сообщение на экран терминала.
ТЕКСТ ПРОГРАММЫ at
1 :
2 # @(#) tree v1.0 Execute command line at specific time
Author: Russ Sage
2а Выполнить командную строку в указанное время
4 if [ $# -lt 2 ]
5 then echo "at: wrong arg count" "&2
6 echo "usage: at hr:min cmd [;cmd ...]" "&2
7 exit 1
8 fi
10 ITS=$1; shift
12 while :
13 do
14 TIME=`date | cut -c12-16`
16 if [ "$ITS" = "$TIME" ]
17 then eval $@
18 exit 0
19 else sleep 35
20 fi
21 done &
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ
ITS Время, в которое следует выполнить указанные команды
TIME Текущее время в системе
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН at?
На протяжении рабочего дня мы выполняем много небольших работ,
которые нужно делать через различные интервалы времени. Некоторые вещи
просто должны быть сделаны один раз в любое время, тогда как другие
должны делаться в определенное время каждый день. Например, вам может
понадобиться запускать процедуру копирования файлов каждую ночь, входить
в другую систему раз в день и проверять почту или сообщения пользователей
сети по заданной теме раз в несколько дней.
Командный файл at предоставляет механизм для выполнения задач,
связанных со временем. Мы можем сказать системе, что и когда мы хотим
сделать. Задача остается "спящей" в фоновом режиме до назначенного
времени. Это дает нам возможность превратить компьютер в будильник,
секретаря, администратора встреч и т.д.
Данная концепция не нова и уже существует в системе Berkeley UNIX
под тем же именем. Она реализована также в последних версиях System V.
Почему же тогда мы представляем здесь нашу собственную версию?
Одна из причин в том, что многие из вас имеют более ранние версии
UNIX, в которых это средство отсутствует. Но важнее, видимо, другое -
наша цель не в том, чтобы сделать существующие команды at устаревшими,
а в показе того, как легко отслеживать время и реализовывать обработку,
связанную со временем. Имея нашу собственную команду at, мы можем
настроить ее по своему вкусу и изменить ее, когда необходимо. Команда
at, представленная здесь, фактически более гибкая, чем at в системе
Berkeley, хотя в первой отсутствуют некоторые особенности второй. Она
более гибкая потому, что вы можете поместить настоящие команды в фоновую
задачу at, в то время как для at в системе Berkeley вы должны
использовать имя командного файла интерпретатора shell. Метод Berkeley
запрещает вам вызывать исполняемые модули непосредственно в командной
строке, а наша at - нет. (Конечно, вы можете с таким же успехом
использовать командные файлы интерпретатора shell, если вам это необходимо.)
ЧТО ДЕЛАЕТ at?
Команда at дает нам возможность собирать несколько команд в одно
целое и впоследствии запускать их. Когда они выполняются, их вывод может
либо идти на экран, либо перенаправляться в определенный файл.
Командная строка принимает два параметра: время выполнения и командную
строку, которую следует выполнить. Время выражено в формате
час:минута. Час должен быть указан строкой из двух цифр (в диапазоне
от 0 до 23 часов), так как его использует команда date. Использование
того же стандарта, что и в команде date, значительно упрощает команду
at. В качестве второго параметра может быть любая команда, которую
обычно можно ввести в командной строке интерпретатора shell. Можно
также использовать конвейеры, составные команды и переназначения. Нет
ограничений на то, какая команда может быть выполнена. Команда at может
запустить обычный исполняемый модуль UNIX или ваш собственный командый
файл.
Выход at по умолчанию направляется в стандартный вывод. Стандартным
выводом в данном случае является экран терминала. Команда at в
системе Berkeley не имеет вывода по умолчанию, что несколько затрудняет
получение результата и отправку его на экран.
Команда at всегда запускается как фоновая задача. Нецелесообразно
запускать ее в приоритетном режиме, где она блокирует терминал на все
время своего выполнения. Пребывая в фоновом режиме, at освобождает
ресурсы, но все же работает для вас. Между прочим, отметим, что когда
процессы ставятся в фоновый режим изнутри командного файла, идентификатор
процесса не печатается на экран, как это происходит, когда процессы
запускаются в фоновом режиме с клавиатуры.
Порождение большого количества фоновых процессов может иметь отрицательный
эффект для системы. Каждая фоновая задача - это цикл while
интерпретатора shell, который работает очень медленно. Когда много фоновых
процессов, мало времени центрального процессора остается на другие
цели. В результате производительность системы ухудшается. В больших
системах это, вероятно, не проблема, если только система не загружена
множеством пользователей, но вы должны использовать это средство
с осторожностью.
Отметим, что формат час:минута годится только для одного полного
дня. Данная программа at задумана как ежедневная и не имеет средств
запуска в определенный день или месяц, хотя вы можете легко расширить
ее, как только поймете, как читается и используется информация о времени.
ПРИМЕРЫ
1. $ at 11:45 echo ^G^G It's almost lunch time
Без пятнадцати минут двенадцать дважды выдается звуковой сигнал
(control-G) и выводится сообщение о ленче.
2. $ at 10:45 "if [ -s $MAIL ]; then echo ^G You have mail; fi"
Без пятнадцати одиннадцать проверяется, существует ли мой почтовый
файл и есть ли в нем хотя бы один символ ($MAIL есть
/usr/spool/mail/russ). Если это так, выдается звуковой сигнал и сообщение
о том, что у меня есть почта.
3. $ at 17:00 "c; date; banner ' time to' ' go home'"
В пять часов вечера очищается экран (с помощью команды c, описанной
далее в данной книге), печатается дата и выводится крупными буквами
на весь экран сообщение "time to go home" ("пора домой"). С помощью
апострофов в командной строке banner мы можем добиться вывода символа
возврата каретки, чтобы разместить каждый набор слов в отдельной строке.
Если какая-либо из этих команд не срабатывает (например, не найдена
команда c), то и весь фоновый процесс оканчивается неудачей.
ПОЯСНЕНИЯ
Прежде всего at проверяет, правильно ли она была вызвана. Строки
4-8 делают проверку ошибок. В командной строке должны присутствовать
по крайней мере два параметра: время и команда. Если это так, то счетчик
позиционных параметров равен 2. Если этот счетчик меньше 2, произошла
ошибка. В стандартный файл ошибок посылаются сообщения об ошибке
с помощью переадресации в файловый дескриптор 2.
Переменная интерпретатора shell ITS инициализируется в строке 10.
В ней устанавливается значение первого позиционного параметра ($1),
которым является час:минута. Как только мы занесли это значение в переменную,
оно больше не нужно нам в командной строке. Команда shift
удаляет $1 из командной строки. Теперь командная строка состоит из вызывающей
команды $0 (т.е. самой at) и остатка строки ($@ или $*). Вызывающая
команда не вычисляется как часть остатка строки, поэтому вам
не нужно заботиться об аргументе $0.
Далее at переходит к вечному циклу while в строках 12-21. Вечным
этот цикл делает команда : (двоеточие). Это встроенная команда интерпретатора
shell, которая ничего не делает кроме того, что всегда возвращает
успешный статус выхода, заставляя тем самым цикл продолжаться.
Команда true интерпретатора shell очень похожа и делает программу более
наглядной. Мы же используем : вместо true, чтобы сократить издержки
на порождение процесса для каждой итерации цикла. Команда : встроена
в сам shell. True, напротив, является внешней командой в каталоге
bin (так же, как ls), она должна быть найдена по файловому пути, выполниться
и вернуть значение. Это занимает гораздо больше процессорного
времени.
На каждой итерации цикла текущее время сверяется с назначенным
временем, переданным из командной строки. Текущее время извлекается из
команды date в строке 14. Обычно date выдает результат в таком формате:

|
| Mon Mar 31 06:54:25 PST 1986
|
|
Поскольку это строка фиксированного размера, мы можем посчитать
номера позиций, в которых размещены час и минута. Данные час:минута
находятся в позициях 12-16. Для получения этих символов мы запускаем
команду date, пропускаем ее результат по конвейеру через cut и вырезаем
нужные позиции. Весь результат присваивается переменной TIME. Заметим,
что поле секунд не используется. Наименьшая единица времени в
этой программе - минута.
Все волшебство данной команды заключено в строках 16-20. Если
время, указанное в командной строке, равно текущему времени (строка
16), вычислить и выполнить остальные аргументы командной строки (строка
17), затем выйти с успешным нулевым значением (строка 18). Если
время не совпало, немного поспать (строка 19) и повторить все сначала.
Символ & в конце цикла в строке 21 превращает весь цикл while в
фоновый процесс. Как мы можем убедиться, что shell выполняет все свои
команды в фоновом режиме и никакие из них не выполняет в оперативном
режиме? В действительности мы не можем этого сделать. Мы должны полагать,
что shell так работает. Поскольку многое при программировании на
shell делается исходя из опыта и интуиции, вам приходится испытывать
многие вещи, чтобы увидеть, как они работают. Периодически shell преподносит
сюрпризы и делает нечто совершенно неожиданное.
ИССЛЕДОВАНИЯ
Что бы случилось, если бы вы поставили задание at в фоновый режим,
а затем вышли из системы? Ответ зависит от того, с каким shell вы
работаете. Если у вас Bourne shell, то ввод команды control-D при выходе
из системы прекращает выполнение всех ваших фоновых задач.
Единственный способ оставить в живых фоновые задачи после выхода из
системы - использовать команду nohup ("no hang up" - "не казнить").
Nohup обеспечивает, что все сигналы о прекращении процесса не достигают
данного процесса. Не получая сигнал о прекращении выполнения, процесс
думает, что вы все еще находитесь в системе. Синтаксис выглядит
так:
nohup at 13:00 echo "back from lunch yet?"
Если вы запускаете Си-shell, все фоновые процессы продолжаются
после вашего выхода из системы. Причина в том, что Си-shell переводит
все свои фоновые задачи в состояние защиты от прекращения выполнения.
Этот процесс автоматический, и его не нужно указывать явно.
Вы, возможно, удивлены тем, что в строке 17 использована команда
"eval $@". Это сформировалось методом проб и ошибок. На начальных этапах
разработки at команда "$@" использовалась сама по себе. При самостоятельном
применении эта команда означает "выполнить все позиционные
параметры". Поскольку это была единственная команда, выполнялась
вся строка позиционных параметров, после чего возникали проблемы.
Использование переназначений и переменных интерпретатора shell сильно
запутывало at.
Для иллюстрации рассмотрим пару примеров. Если мы запускаем at с
командной строкой
at 09:30 echo $HOME
то все вроде бы работает. Сначала раскрывается переменная $HOME, затем
echo печатает ее значение - /usr/russ. Но если мы запускаем командную
строку
at 09:30 echo \$HOME
то переменная не раскрывается и echo фактически печатает $HOME вместо
значения переменной $HOME. Мы избежали этого просто с помощью команды
eval для повторного вычисления командной строки перед ее выполнением.
Существо проблемы в том, что вызывающий интерпретатор shell не раскрывает
значение переменной, поэтому мы заставляем выполняющийся shell
повторно анализировать командную строку и вычислять все переменные. На
этот раз значения переменных раскрываются, и мы получаем верный конечный
результат.
МОДИФИКАЦИИ
Возможно, вы захотите более подробно рассмотреть интерфейс со
временем. В нынешнем состоянии at воспринимает только время в пределах
от 0 до 23 часов в течение одного дня. Неплохим дополнением было бы
заставить его различать время суток, т.е. 8:30 a.m. (до полудня) или
8:30 p.m. (после полудня). Было бы неплохо также иметь возможность
сказать "через 10 минут сделать то-то и то-то". В этом случае команда
могла бы иметь примерно такой вид:
at -n 10 echo "do in now plus 10 minutes"
где -n было бы текущим временем, а 10 добавлялось бы к нему.
Другой очевидной областью модификации является наделение at возможностью
запоминания периодов времени, превышающих сутки. Это может
быть завтрашний день, определенный день или даже определенный месяц.
Работа с определенным месяцем может быть не совсем реальной, поскольку
командный файл, выполняемый в фоновом режиме в течение нескольких
месяцев, потребует громадного количества процессорного времени, а также
хранения постоянно возрастающего счетчика идентификаторов процессов,
что даст, вероятно, пользователям искаженное представление об
активности системы. По достижении максимального номера процесса, снова
вернутся младшие номера, так что это не приведет к каким -либо серьезным
последствиям. Решение вопроса о том, стоит ли такой ценой достигать
вашей цели, зависит от того, считаете ли вы, что ваши требования
излишне загружают систему.

ИМЯ: b

b Обработчик фоновых задач
ФОРМАТ ВЫЗОВА
b any_command_with_options_and_arguments
(любая команда с опциями и аргументами)
ПРИМЕР ВЫЗОВА
b cg f.c
Компилировать исходный файл в фоновом режиме, где cg - командная
строка компилятора, описанная в главе 10.
ТЕКСТ ПРОГРАММЫ b
1 :
2 # @(#) b v1.0 Background task handler Author: Russ Sage
2а Обработчик фоновых задач
4 ($@; echo "^G\ndone\n${PS1}\c") &
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН b?
Как вы видели в последнем разделе, Bourne shell дает возможность
запускать задачи в фоновом режиме выполнения. Это делает символ &. Что
же на самом деле происходит, когда мы запускаем что-нибудь в фоновом
режиме? Порождается еще один shell, который должен выполнить свою
собственную командную строку. После того, как все его команды выполнятся,
он завершается. Вы можете определить фоновые задачи по результату
работы команды ps. Эти задачи выглядят как интерпретаторы shell,
запущенные с вашего терминала, однако их владельцем, или родительским
процессом в действительности является команда init, а не ваш регистрационный
shell (это справедливо только для shell, к которым применена
команда nohup). Интерпретаторы shell, к которым не применялась nohup,
принадлежат вашему регистрационному shell. Ниже приводится пример
распечатки команды ps для фоновых задач. Командой для выполнения в фоновом
режиме была:
while :;do date; done &
Команда ps показывает мой регистрационный shell (PID=32), введенную
мной командную строку для выполнения в фоновом режиме (PID=419) и
shell, который выполняет цикл while (PID=449).

|
| UID PID PPID C STIME TTY TIME COMMAND
|
| root 0 0 0 Dec 31 ? 0:03 swapper
| root 1 0 0 Dec 31 ? 0:02 /etc/init
| russ 32 1 0 14:18:36 03 1:26 -shV
| russ 419 32 0 15:30:31 03 0:02 -shV
| russ 449 419 2 15:30:31 03 0:02 -shV
|
Ниже приведен листинг команды ps, который показывает фоновый
shell, принадлежащий процессу init. Он был получен командой "b ps
-ef", где b - утилита, которая будет рассмотрена далее. Как видите,
последний процесс 471 есть фоновый shell, принадлежащий процессу 1,
которым является init, а не мой регистрационный shell (PID=32).

|
| UID PID PPID C STIME TTY TIME COMMAND
| root 0 0 1 Dec 31 ? 0:04 swapper
| root 1 0 0 Dec 31 ? 0:02 /etc/init
| russ 32 1 1 14:18:36 03 1:30 -shV
| russ 472 471 5 15:46:46 03 0:12 ps -ef
| russ 471 1 0 15:46:46 03 0:00 -shV
|
К чему все это приводит? Когда мы используем фоновые задачи, мы
должны мириться с "неразборчивостью" при управлении асинхронными процессами.
Каковы эти недостатки?
Во-первых, мы никогда не знаем момента завершения фоновых задач.
Единственный способ определить момент завершения таких задач - проверка
результатов в каком-либо файле или некоторой работы, выполненной
задачей, или использование команды ps и постоянное слежение за тем,
когда процесс завершится. Такое слежение при помощи команды ps - не
самый лучший способ, поскольку ps занимает много процессорного времени
и очень медленно работает.
Второй неаккуратный момент - это символ приглашения после выдачи
на ваш экран результата из фоновой задачи. После того как выдан результат
из фоновой задачи, ваш регистрационный shell ожидает ввода команды,
но приглашения может и не быть, поскольку оно было удалено с
экрана некоторым другим сообщением. Вы можете ожидать приглашения целый
день, но оно никогда не появится, поскольку оно уже было выведено
на экран. Вы просто должны знать, что shell ждет вашу команду.
Нам необходимо инструментальное средство, которое сообщает нам о
завершении фоновой задачи, а также восстанавливает наш экран после выдачи
на него каких-либо результатов. Можем ли мы сказать, выполняла ли
вывод на экран фоновая задача или нет? Нет, поэтому мы должны жестко
запрограммировать восстановление экрана в программе.
ЧТО ДЕЛАЕТ b?
Командный файл b - это механизм, который помогает в выполнении
фоновых задач. Он запускает наши фоновые задачи. По завершении он
отображает на экран слово "done" и затем повторно выводит символ-приглашение
shell.
Данное средство не имеет опций и проверки на наличие ошибок. Обработчик
фоновых задач фактически выполняет командную строку, которую
мы ему передаем, и последующую обработку. Отметим, что для выдачи на
экран вашего символа приглашения, вы должны экспортировать переменную
PS1 из вашей текущей среды. Это может соблюдаться не на всех машинах,
поскольку каждая система UNIX имеет свои особенности. В системе XENIX
переменная PS1 не передается, возможно из-за того, что это shell, который
вызывает другой shell в фоновом режиме. Если вы скажете "sh" в
интерактивном режиме, он будет передан как приглашение. Система UNIX
так прекрасна и удивительна!
ПРИМЕРЫ
1. $ b ls -R ..
Начиная с родительского каталога, рекурсивно составляется список
всех файлов и выводится на экран. Обратите внимание, что при использовании
фоновых задач вы не можете эффективно передать все это по конвейеру
команде more, поскольку обычным входным устройством для фоновых
задач является /dev/null. Команда more не работает нормально, когда ее
вызывают из фонового режима. Это также имеет смысл, поскольку вы могли
бы иметь две задачи - одну в фоновом режиме, а другую в приоритетном -
производящие беспорядок на экране. Фоновая команда more должна была бы
сохранять в неприкосновенности то, что выводит на экран приоритетная
команда.
2. $ b echo hello " z
Файл z содержит не только слово "hello", но также и сообщение
"done", поскольку переадресация в файл z выполняется во внешнем shell.
Переадресация для подзадачи должна быть выполнена в круглых скобках
программы b, а мы в данном случае не можем этого сделать.
3. $ b sleep 5; echo hello
Эта командная строка не может быть выполнена, поскольку программа
b воспринимает только команду sleep. Команда echo не помещается в фоновый
процесс и сразу же выполняется.
4. $ b "sleep 5; echo hello"
Эту командную строку мы тоже не можем выполнить, поскольку эти
две команды будут восприняты командным файлом b как одна. Затем команда
sleep не выполнится, поскольку "5; echo hello" является недопустимым
указанием временного периода для команды sleep.
ПОЯСНЕНИЯ
Обратите внимание, что в строке 4 вся структура команды заключена
в круглые скобки, за которыми следует символ &. Круглые скобки передают
всю структуру подчиненному shell, который затем помещается в фоновый
режим выполнения. Помещая все команды в один shell, мы гарантируем
вывод на экран слова "done" после завершения последнего процесса.
Данная командная строка выполняется с помощью символов $@. Это
означает: "выполнить всю командную строку, расположенную справа".
Поскольку выражение $@ выполняет само себя (т.е. не в операторе echo
или в чем-либо подобном), то shell просто выполняет команды исходной
командной строки. Это именно то, что мы хотим! Обратите внимание, что
здесь нет никакого оператора eval. Поскольку то, что мы делаем, похоже
на своего рода "командный интерпретатор строк" для их ввода и исполнения,
вы могли бы подумать, что команда eval здесь необходима. По опыту
мы знаем, что это не так. Похоже, что применение eval усложнит дело.
Даже наш старый тест, использующий переменные среды выполнения, работает.
По команде
b echo $HOME
на экран будет выдано сообщение
/usr/russ
Когда вся команда выполнится, подается звуковой сигнал и выводится
сообщение, информирующее пользователя о том, что операция завершилась.
Поскольку это сообщение накладывается на то, что было на экране,
то переотображается начальный символ приглашения (PS1). Это делает
нормальным вид экрана в том смысле, что символ приглашения shell сообщает
об ожидании ввода.

ИМЯ: greet

greet Своевременное приветствие с терминала
НАЗНАЧЕНИЕ
Определение времени суток и печать приветствия и какого-либо
сообщения на терминал в зависимости от времени дня.
ФОРМАТ ВЫЗОВА
greet
ПРИМЕР ВЫЗОВА
greet Вызывает командный файл greet, который определяет
время и печатает соответствующее сообщение.
ТЕКСТ ПРОГРАММЫ greet
1 :
2 # @(#) greet v1.0 Timely greeting from the terminal
Author: Russ Sage
2а Своевременное приветствие с терминала
4 if [ `expr \`date +%H\` \" 12` = "1" ]
5 then echo "\nGood morning.\nWhat is the best use of your
time right now?"
6 elif [ `expr \`date +%H\` \" 18` ="1" ]
7 then echo "\nGood afternoon.\nRemember, only handle a piece
of paper once!"
8 else echo "\nGood evening.\nPlan for tomorrow today."
9 fi
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН greet?
Одним из замечательных преимуществ многопользовательских операционных
систем является то, что они имеют хорошо развитую концепцию времени.
Обычно они содержат часы реального времени и некоторое программное
обеспечение, которое манипулирует с ними. Однако всегда есть место
для дополнительного программного обеспечения, работающего со временем.
Такие средства могут быть написаны как на языке Си, так и на
shell-языке.
Как мы извлекаем и выделяем время с помощью командного файла интерпретатора
shell? Доступно много способов, но стандартная команда
UNIX date, видимо, является наилучшим способом. В случае языка Си вы
должны программно управлять преобразованием времени и временными зонами.
Команда date делает это для вас.
Важна также единица времени. Должны ли мы различать секунды, минуты,
часы, дни или недели? Это все зависит от требуемого приложения.
В нашем простом примере мы различаем только три части суток: утро,
день и вечер. Мы определили эти периоды так: с полуночи до полудня, от
полудня до шести часов и от шести часов до полуночи соответственно.
ЧТО ДЕЛАЕТ greet?
Greet - это утилита, которая приветствует пользователя различными
сообщениями в зависимости от времени суток. Выводимые сообщения не так
важны. Они в основном использованы как примеры, показывающие, как могут
быть выполнены какие-то команды. Если вы работаете в одиночестве и
хотели бы поболтать, эти сообщения могли бы читаться периодически из
соответствующих файлов для создания иллюзии автоматической письменной
болтовни в зависимости от времени суток.
Действительной же целью является создание каркаса программы, которая
может переключаться в зависимости от параметров времени. Путем
расширения концепции времени вы можете создать другие утилиты, которые
знают, когда им работать (в какой промежуток времени) и могут вести
себя иначе в соответствии со временем.
Greet не требует ничего в командной строке. Не выполняется никакой
проверки на наличие ошибок, поэтому и нет в программе синтаксической
подсказки. Выход команды greet может быть переадресован в файл
или передан по конвейеру другому процессу.
ПРИМЕРЫ
1. $ if greet | fgrep 'morn' " /dev/null
" then morning_routine
" fi
Выполняется greet. Стандартный вывод greet по конвейеру передается
на стандартный ввод fgrep. Производится поиск символьной строки
"morn". Весь выход переадресовывается в никуда, так что он не засоряет
экран. Если выходной статус команды fgrep равен нулю (она нашла нужную
строку), выполняется файл morning_routine.
2. $ at 10:30 greet; at 13:50 greet
Вы могли бы вставить это в ваш .profile. Два процесса at будут
выполняться на вашей машине в фоновом режиме до тех пор, пока не
наступит время их запуска - тогда они поприветствуют вас на вашем терминале.
Правда, это может причинить небольшое неудобство. Сообщение
может появиться, когда вы работаете в редакторе, и нарушить содержимое
экрана, но на самом деле оно не изменит ваш редактируемый файл.
ПОЯСНЕНИЯ
Вся программа представляет собой один большой оператор if
-then-else в строках 4-9. Логика программы выглядит на псевдокоде следующим
образом:
if it is morning если утро
then echo morning statement то вывести "утреннее"
приветствие
else if it is noon иначе если день
then echo noon statement то вывести "дневное"
приветствие
else echo evening statement иначе вывести "вечернее"
приветствие
В действительности программа гораздо сложнее, поэтому переведем
дыхание и приступим к делу.
В строке 4 проверяется текущее время на то, меньше ли оно 12
часов. Если да, то фраза команды expr выводит на стандартное устройство
вывода единицу ("1"). Поскольку символы ударения (`), которые
обрамляют эту фразу, перехватывают стандартный вывод, символ 1 становится
частью оператора проверки, что указано квадратными скобками
([]). Затем оператор test проверяет, равен ли выход команды expr литеральной
единице. Если они одинаковы, то в строке 5 выводится "утреннее"
сообщение.
Рассмотрим более подробно, как раскрывается оператор expr.
Во-первых, он заключен в символы ударения. Это означает, что он выполняется
перед оператором проверки. Затем его выход помещается для обработки
в оператор test. Однако внутри оператора expr имеется еще одно
выражение между знаками ударения, которое выполняется до оператора
expr. Такое старшинство выполнения управляется интерпретатором кода
внутри shell.
Внутренние знаки ударения сохраняются при начальном синтаксическом
разборе строки, поскольку они экранированы символами обратной
косой черты. Первой запускается команда date, имеющая в качестве выхода
только текущее значение часа в соответствии с форматом %H. Затем
expr использует данное значение часа для проверки, меньше ли оно 12.
Если да, expr печатает единицу. Если значение часа больше или равно
12, то возвращаемое значение равно 0. Такое понимание, что 1=истина и
0=ложь, соответствует синтаксису, используемому в языке Си.
Однако ранее мы замечали, что в среде программирования на языке
shell 1 означает ложь, а 0 - истину. Это происходит потому, что проверяемое
значение оператора if является в действительности статусом выхода
из предварительно выполненной команды. Нуль соответствует нормальному
завершению, поэтому 0 использован для переключения проверки в
состояние "истина" и выполнения оператора then. Для того, чтобы преобразовать
возвращаемый статус 1 (при условии, что значение часа меньше
12) в нуль (для переключения оператора then), мы используем команду
test. Возвращаемый статус единицы равен константе 1, поэтому команда
test возвращает 0, что представляет истину. Вот так!
Если бы не были использованы вложенные знаки ударения, то
единственным способом передачи данного типа информации другому процессу
было бы применение переменных shell. Использование вложенной командной
подстановки дает нам большую гибкость и простоту программирования.
Чем больше глубина вложенности, тем глубже экранирование знаков
ударения. Порядок экранирования символами обратной косой черты такой:
не нужно для внешней команды, один раз для второй внутренней команды,
пять раз для третьей внутренней команды. На четвертом уровне их должно
быть семь или девять (я еще не пробовал), но вероятно нет большой нужды
во вложенности такой глубины.
Если проверка в строке 4 дает "ложь", выполняется строка 6. Это
оператор else от первого if и одновременно следующий if. В таких особых
случаях синтаксис shell меняется. Ключевое слово "else" становится
ключевым словом "elif".
Второй if использует команду test точно так же, как и первый.
Проверяемое время здесь 18, что представляет собой 6 часов вечера.
Если вторая проверка также дает "ложь", выполняется последний оператор
в строке 8. Этот else не использует команду test, поскольку после выполнения
первых двух проверок мы можем сделать вывод, что остался
последний период времени, а именно период после 18:00.

ИМЯ: lastlog

lastlog Сообщает время последней регистрации
НАЗНАЧЕНИЕ
Записывает и выводит на экран день и время вашей последней регистрации
в системе.
ФОРМАТ ВЫЗОВА
lastlog [-l]
ПРИМЕР ВЫЗОВА
lastlog Печатает дату, когда вы последний раз регистрировались
ТЕКСТ ПРОГРАММЫ lastlog
1 :
2 # @(#) lastlog v1.0 Report last login time Author: Russ Sage
2а Сообщает время последней регистрации
4 if [ $# -gt 1 ]
5 then echo "lastlog: arg error" "&2
6 echo "usage: lastlog [-l]" "&2
7 exit 1
8 fi
10 if [ "$#" -eq "1" ]
11 then if [ "$1" = "-l" ]
12 then date "" $HOME/.lastlog
13 lastlog
14 else echo "lastlog: unrecognized option $1" "&2
15 echo "usage: lastlog [-l]" "&2
16 exit 1
17 fi
18 else echo "Time of last login : `tail -2 $HOME/.lastlog |
19 (read FIRST; echo $FIRST)`"
20 fi
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ
FIRST Хранит первую из двух введенных строк
HOME Хранит имя вашего регистрационного каталога
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН lastlog?
Одним из преимуществ работы в системе UNIX является то, что в ней
совершается автоматическая запись вашего начального времени при каждом
сеансе работы - вашего времени регистрации. Эта информация может быть
полезной по нескольким причинам. Вопервых, вы можете запомнить, когда
вы действительно работали в системе последний раз и проверять, не регистрировался
ли кто-нибудь под вашим паролем во время вашего
отсутствия. Как мы увидим в главе 9, имеется ряд возможностей для того,
чтобы кто-нибудь мог "заимствовать" ваш пароль без спроса. По этой
причине многие коммерческие системы сообщают вам, когда вы регистрировались
последний раз (или когда, по их мнению, вы это делали).
Другой возможной причиной мог бы быть подсчет общего времени в
конце сеанса работы. Вы могли бы использовать это как учетную информацию
для себя или вычислительного центра. Немного позже мы представим
средство, которое помогает при таких подсчетах.
Разрабатываемое нами инструментальное средство должно иметь возможность
записывать новые значения времени и выводить на экран время
нашей последней регистрации. Важно, что данная программа может быть
вызвана так, что она не изменяет файл с данными, но постоянно выводит
время последней регистрации.
ЧТО ДЕЛАЕТ lastlog?
Lastlog - это программа, которая записывает время вашей регистрации
при каждом входе в систему. Затем это время хранится в файле данных
в вашем регистрационном каталоге под именем $HOME/.lastlog. Имя
файла lastlog начинается с точки с той целью, чтобы сделать его невидимым
для команды ls. Укрытие "служебных" файлов от распечатки по
умолчанию несколько предохраняет от любопытных глаз, а также убирает
эти файлы с дороги, когда вы просматриваете что-то другое.
При вызове без опций lastlog печатает для нас дату последней регистрации,
получая запись из файла .lastlog.
Для выполнения новой записи в файл .lastlog необходимо вызвать
lastlog с опцией -l. При этом новое значение времени запишется в файл
.lastlog, а затем командный файл lastlog вызовет сам себя для вывода
на экран нового значения - небольшая рекурсия.
Для того, чтобы программа lastlog работала автоматически, вы
должны выполнять ее из вашего файла .profile во время регистрации. При
таком способе она запишет последнее время в файл .lastlog. В качестве
примера посмотрите файл .profile в первой главе.
ПОЯСНЕНИЯ
В строках 4-8 выполняется проверка на наличие ошибок. Если вы
вызвали lastlog с числом аргументов больше одного, то это приведет к
ошибке. Выводится сообщение на стандартное устройство регистрации ошибок,
и lastlog завершается со статусом ошибки 1.
Строки 10-20 представляют собой оператор if-then-else, который
показывает, был ли это вызов для записи нового значения времени или
для печати старых значений.
Если в строке 10 число позиционных параметров равно одному, то мы
знаем, что либо этот параметр должен быть опцией -l, либо это ошибка.
Следующий оператор if в строке 11 проверяет, является ли первый позиционный
параметр опцией -l. Если да, то в файл $HOME/.lastlog добавляется
текущая дата и lastlog вызывается снова без аргументов для печати
предыдущей даты регистрации. (Мы только что видели, как это делается.)
Если это не был аргумент -l, то строки 14-16 выполняют обработку ошибки.
Если число позиционных параметров равно нулю, выполняется оператор
else в строке 18. Отсутствие опций означает, что мы хотим найти
время нашей последней регистрации на машине и распечатать его. Это кажется
довольно простым, но кто сказал, что машины просты?
Если вы помните последовательность работы, то мы сперва регистрируем
новое время, а затем хотим найти время нашей предыдущей регистрации.
Для файла .lastlog это означает, что наше текущее время регистрации
находится в самом конце файла, а наше предыдущее время регистрации
находится в строке непосредственно перед ним. Это значит, что мы должны
получить вторую строку от конца файла. Да уж.
Как видно из строки 18, она занимается получением последних двух
строк. Команда tail красиво выполняет эту работу. Нам нужен такой
способ, чтобы мы могли прочитать именно первую строку, а вторую отбросить,
что выполняется в строке 19. Мы передаем по конвейеру выход
команды tail подчиненному shell (указанному круглыми скобками), который
читает первую строку и затем отображает ее. А что же со второй
строкой? Она никогда не берется и пропадает. Другим способом может
быть передача выхода команды tail по конвейеру команде "head -1".
Поскольку эта команда не имеет других опций, мы не даем никаких
примеров. Тем не менее, давайте теперь рассмотрим наше другое средство
регистрации времени входа в систему.

ИМЯ: timelog

timelog Учет и статистика времени
НАЗНАЧЕНИЕ
Интерфейсное меню для слежения и сопровождения файлов регистрации
времени.
ФОРМАТ ВЫЗОВА
timelog
ПРИМЕР ВЫЗОВА
timelog Выводит на экран главное меню, из которого можно
выбирать необходимое действие
ТЕКСТ ПРОГРАММЫ timelog
1 :
2 # @(#) timelog v1.0 Time accounting and statistics
Author: Russ Sage
2а Учет и статистика времени
4 PROJ=""
6 while :
7 do
8 set `date`
9 echo "
11 $1, $2 $3 $4
13 Time Logger
14 ----------- Project: $PROJ
15 s) Select a project file
16 c) Create a new project file
17 l) List current project files
18 v) View the project file
19 n) Turn billing on
20 f) Turn billing off
21 r) Report ststistics
23 enter response (s,c,l,v,n,f,r,): \c"
25 read RSP
27 case $RSP in
28 "") break;;
29 s) echo "\Enter project name ( for exit): \c"
30 read PROJ2
31 if [ "$PROJ2" = "" ]
32 then continue
33 fi
34 if [ ! -s $PROJ2.time ]
35 then echo "you must specify a valid project
file"
36 continue
37 fi
38 PROJ="$PROJ2";;
39 c) echo "\nEnter the new project name ( to
exit): \c"
40 read PROJ2
41 if [ "PROJ2" = "" ]
42 then continue
43 fi
44 if [ -f "$PROJ2.time" ]
45 then echo "\n ** $PROJ2 already exists **"
46 continue
47 fi
48 PROJ="$PROJ2"
49 echo "\nProject file created: $PROJ"
50 echo "Project file created: `date`\nOFF: begin"
" $PROJ.time;;
51 l) echo "\nCurrent project files:\n"
52 ls -l *.time 2"/dev/null || echo "no project
files" |
53 sed "s/\.time//";;
54 v) if [ "$PROJ" = "" ]
55 then echo "you must select a project file
first"
56 continue
57 fi
58 echo "\n:
"
59 more $PROJ.time
60 echo ":
";;
61 n) if [ "$PROJ" = "" ]
62 then echo "you must select a project file
first"
63 continue
64 fi
65 if [ "`tail -1 $PROJ.time|cut -d: -f1`" !=
"OFF" ]
66 then echo "logging was not turned off"
67 continue
68 fi
69 echo "\nBilling turned on for project file:
$PROJ"
70 echo "ON: `date`" "" $PROJ.time;;
71 f) if [ "$PROJ" = "" ]
72 then echo "you must select a project file
first"
73 continue
74 fi
75 if [ "`tail -1 $PROJ.time|cut -d: -f1`" !=
"ON" ]
76 then echo "logging was not turned on"
77 continue
78 fi
79 echo "\nBilling turned off for project file:
$PROJ"
80 echo "OFF: `date`" "" $PROJ.time;;
81 r) while :
82 do
83 echo "
84 Statistics
85 ---------- Project: $PROJ
86 a) Accumulative time totals
87 n) All times on
88 f) All times off
90 enter response (a,n,f,): \c"
92 read RSP
94 case $RSP in
95 "") break;;
96 a) awk '/Total:/ { PRINT $0 }'
$PROJ.TIME;;
97 n) awk '/ON/ { print $0 }'
$PROJ.time;;
98 f) awk '/OFF/ { print $0 }'
$PROJ.time;;
99 *) echo "\n ** Wrong command,
try again **";;
100 esac
101 done;;
102 *) echo "\n ** Wrong command, try again **";;
103 esac
104 done
ПРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ
PROJ Содержит текущее имя проекта
PROJ2 Содержит временное имя проекта, введенное пользователем
RSP Содержит команду выбора из меню
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН timelog?
Время - драгоценный товар. Его всегда не хватает, и если уж оно
использовано, то его никогда больше нельзя вернуть. Мы хотим быть уверены,
что наше время используется плодотворно. Мы можем и должны создать
инструментальные средства, которые помогут нам управлять нашим
временем и фиксировать его.
Мы должны рассмотреть следующие вопросы: над какими проектами мы
работаем, в течение какого времени мы над ними работаем (т.е. начало и
окончание), и какую еще информацию нам нужно хранить.
Как раз почти все эти функции поддерживаются программой timelog.
Мы написали довольно длинную программу, но заметим, что на самом деле
работа над этим средством не окончена. Предлагаемый командный файл
timelog подготавливает вас к работе с системой управления временем.
Вам нужно вставить ваш собственный текст для выдачи отчетов, основанных
на статистике времени.
ЧТО ДЕЛАЕТ timelog?
Timelog относится к весьма важной области, связанной с фиксацией
времени и управлением временем. Отметим, что количество учетных сведений,
которые можно создать, просмотреть и обработать, ограничено только
доступным пространством файловой памяти.
Timelog - это полностью управляемый с помощью меню интерфейс.
Системы с меню в UNIX - это нечто новое, они имеют свои преимущества и
недостатки. Одним из преимуществ является то, что вся работа над данными
выполняется программно, а не вручную. Кроме того, каждую функцию
наглядно видно и легко выбрать. Вам нет необходимости запоминать опции
и имена файлов, достаточно просто нажать одну клавишу для выполнения
действия.
Недостатком является то, что меню работают медленнее, чем ручной
интерфейс (т.е. просто набор и непосредственное выполнение команд).
Это очень важное замечание, но мы должны также помнить, что программы
должны быть простыми в использовании, простыми для модификации и выполнять
множество мелочей, связанных с какой-либо идеей или областью
назначения. Потеря машинного времени чаще всего лучше, чем потеря времени
человека! Другой недостаток - для того чтобы добраться до определенной
функции, вы должны пройти через несколько уровней меню.
Например, чтобы напечатать отчет, вы должны вызвать timelog, выбрать
меню статистики, затем выбрать нужный вам отчет. Здесь три уровня,
а при наличии утилиты вы могли бы всего одной командой сказать
"report report_file".
Для утилит, выполняющих одну функцию, наличие одной команды с
несколькими опциями довольно эффективно. Такой подход применяется в
большинстве командных файлов интерпретатора shell. Но когда у вас есть
множуство небольших задач, выполняемых над группой объектов, меню более
удобны.
Некоторые системы предоставляют интерфейс, управляемый как меню,
так и командами. Это устраивает больший круг пользователей и позволяет
избежать большинства недостатков, упомянутых выше. Конечно, при таком
подходе неминуемы некоторые издержки и программа становится более
длинной.
При вызове timelog на экран выводится начальное меню, как показано
ниже.

|
| Thu, Jun 19 21:32:12
|
| Time Logger
| ----------- Project:
| s) Select a project file
| c) Create a new project file
| l) List current project files
| v) View the project file
| n) Turn billing on
| f) Turn billing off
| r) Report statistics
|
| enter response (s,c,l,v,n,f,r,):
В левом верхнем углу показан день недели и дата. В правом верхнем
углу показано время. Это реальное время, и оно обновляется при каждом
вызове меню. Имя меню "Time Logger" (регистратор времени). "Report
statistics" (сообщить статистику) вызывает появление подчиненного меню.
Строка, в которой написано "Project:" (проект), показывает, что
текущее имя проекта нулевое. Для того чтобы работать над проектом, вы
сперва должны создать файл проекта или выбрать его, если он уже существует.
Все действия, выполняемые после этого, относятся к текущему
файлу проекта.
Первый пункт меню s предназначен для выбора файла проекта. После
выбора этого пункта выводится сообщение:

|
| Enter project name ( for exit):
| Введите имя проекта ( для выхода):
Вы можете ввести любую текстовую строку в качестве имени проекта
или, если вам не нужна эта опция, нажать клавишу возврата каретки для
благополучного выхода. Если вы не помните имена проектов, вы можете
использовать опцию l, поясняемую ниже. После ввода имени существующего
проекта, текущему имени проекта (которое печатается справа вверху в
каждом меню) присваивается имя этого файла.
Следующей является опция c для создания файла проекта. Как уже
отмечалось, это должно быть первым, что вы делаете, начиная работать с
утилитой timelog, но после этого вы обычно выбираете существующие файлы.
Когда вы выбрали опцию c, печатается следующее приглашение:

|
| Enter the new project name ( to exit):
| Введите имя нового проекта ( для выхода):
Здесь нужно вводить то же самое, что и при выборе проекта. Для
выхода нажмите возврат каретки. После ввода имени текущее имя проекта
изменяется, создается файл проекта, запоминается время, и файл загружается
исходной информацией.
Следующая опция l предназначена для выдачи списка имен файлов
проектов. Поскольку каждый проект является файлом, отображается список
в виде, обычном для команды ls. Тем не менее, будьте внимательны.
Список нельзя получить прямо командой ls. Имена изменены для защиты от
наивных.
Каждый файл проекта хранится на диске в формате "project. time".
Часть project в каждом файле отличается и представляет собой имя, введенное
в опции создания. Все файлы имеют суффикс .time. Когда выводится
список, префикс .time отбрасывается, так что имена файлов являются
просто проектами, которые вы ввели в опции выбора проекта. Здесь
все работает, но вы должны помнить, что если вы захотите просмотреть
файлы времен вручную, то имена не будут теми же самыми. Если нет никаких
файлов проектов, то об этом выводится сообщение.
Следующей опцией является v для просмотра файла проекта. Файлом,
который вы собираетесь просмотреть, является текущий файл проекта. Его
имя выводится в меню справа от слова "Project:". Если не появилось никакого
имени, вы должны сперва создать новый проект или выбрать существующий.
Файл проекта выводится на экран командой UNIX more.
Следующей опцией является опция n для включения подсчета времени.
Это означает начало записи нового сеанса работы над проектом. Проверяется
имя проекта, чтобы выяснить, был ли выбран файл проекта. Если
нет, выводится сообщение о том, что нужно это сделать. Затем проверяется,
был ли файл проекта отключен предыдущей операцией. Если да, то
регистратор времени может быть включен. Вы не можете включить его
дважды. Вы должны отключить его, затем включить и т.д.
Следующая опция f отключает подсчет времени для файла проекта.
Текущее имя проекта сравнивается с нулевым, и если это так, то выводится
соответствующее сообщение. Затем проверяется, был ли предварительно
включен подсчет времени для этого файла. Если был, то в файл
проекта добавляется запись о выключении подсчета.
Последней опцией является r для отчета и статистики. После ее выбора
на экран выводится подчиненное меню:

|
| Statistics
| ---------- Project:
| a) Accumulative time totals
| n) All times on
| f) All times off
|
| enter response (a,n,f,):
Как упоминалось ранее, это меню на самом деле не реализовано.
Несколько команд-заглушек позволяют этому меню функционировать, но в
этом месте вы можете настроить отчеты по вашим требованиям. Обратите
внимание, что имя проекта также выводится в этом меню. Это имя затем
доступно для любых функций, помещенных в данное меню.
ПРИМЕРЫ
1. c,l,v
Это первый набор команд при первоначальном запуске. Опция c -
пункт меню для создания файла проекта. Команда l выводит список всех
имен файлов проектов, а v просматривает исходные данные, находящиеся в
файле проекта.
2. n,n
Такая последовательность иллюстрирует проверку на ошибки внутри
программы. Сначала включается подсчет времени для текущего файла проекта,
а затем он включается опять. Timelog распознает это и сообщает,
что вы должны отключить подсчет перед тем, как снова включить его.
3. s,junk
Эта последовательность также иллюстрирует проверку ошибок. Пытаемся
выбрать новое имя файла проекта. Имя файла junk (которого вы не
имеете). Timelog проверяет, существует ли файл регистрации времени с
именем junk. Если нет, выводится сообщение о том, что вы должны выбрать
правильное имя файла проекта.
ПОЯСНЕНИЯ
В этом командном файле много текста, но замысел не очень сложен.
Строка 4 инициализирует переменную PROJ нулевым значением. PROJ -
это переменная, которая содержит имя проекта, отображаемое в меню. В
начале работы мы должны быть уверены, что эта переменная установлена в
нуль.
Строки 6-104 - это огромный бесконечный цикл while, который выполняет
работу всей программы. Поскольку это бесконечный цикл, мы можем
выйти из него либо оператором break, (вводя обычные символы прерывания),
либо с помощью команды выхода.
В строке 8 позиционным параметрам присваивается результат команды
date. Поступая таким образом, мы можем затем легко обращаться к каждому
полю без выделения его командой cut - shell выполняет за нас синтаксический
разбор полей. Мы можем ссылаться на поля даты в виде $1,
$2 и т.д.
Строки 9-23 выводят на экран главное меню. Верхняя строка обращается
к данным из команды date. $1, $2 и $3 представляют собой день недели,
месяц и число. $4 - это время. Перед тем как команда echo выводит
текстовые строки, эти переменные раскрываются таким образом, что
они появляются в меню. Если переменная PROJ равна нулю, то ничего не
печатается в качестве имени текущего проекта. Символы \c в конце огромного
оператора echo устанавливают курсор после приглашения в этой
же строке, так что мы готовы принимать вводимые пользователем символы.
После печати меню в переменную RSP читается ответ в строке 25.
Затем наступает черед огромного оператора case (строки 27-103), который
содержит ветку для каждой команды.
В строке 28 проверяется, не был ли ответ всего лишь возвратом каретки,
указывающим, что пользователь хочет выйти. Если был, то цикл
while завершается посредством команды break и программа заканчивает
работу.
Иногда возврат каретки - более желательный метод выхода, чем команда
exit интерпретатора shell. В конечном итоге эта команда shell
приводит к выполнению программ exit и _exit Си -интерфейса. Выполнение
вызова exit в Си иногда приводит к неожиданным побочным эффектам, в то
время как нормальное выполнение текста программы до конца не дает таких
же результатов. Однажды мы столкнулись с такой проблемой при
использовании ESC -последовательностей для изменения цвета на цветном
мониторе. Когда программа завершалась нормально, цвет не переустанавливался.
Однако когда был сделан системный вызов exit, печатались некоторые
ESC-последовательности, что переустанавливало отдельные части
экрана. Очень странно!
Строки 29-38 управляют функцией выбора проекта. Имя проекта запрашивается
и читается в переменную PROJ2. PROJ2 использована для временного
хранения этого значения. Если был введен возврат каретки, оператор
continue приводит к следующей итерации внешнего цикла while. Это
позволяет пользователю прекратить выполнение этой функции при ошибочном
вводе, оставаясь все же в timelog. Если ввод был непустым, файл
проекта проверяется на существование и на наличие в нем данных. Если
файл не существует, пользователя просят указать верное имя проекта.
Если имя файла правильное, то переменной PROJ присваивается значение
PROJ2. Только после того, как командный файл с уверенностью знает, что
имя, введенное пользователем, допустимо, оно назначается в качестве
текущего имени проекта. Это предохраняет от потери выбора текущего
проекта из-за ошибки пользователя. Теперь PROJ выводится в меню на экран.
Команда создания обрабатывается строками 39-50. Снова запрашивается
имя и проверяется, не равно ли оно нулю. Если имя было введено,
то проверяется, существует ли уже такой файл. Мы не хотим снова создавать
и затирать уже имеющийся файл. Файл создается в строке 50. В этот
файл выводятся отметка о времени его инициализации и начальное сообщение
о том, что подсчет времени отключен.
Опция вывода списка выполняется в строках 51-53. Выводится заголовок,
а затем команда ls используется для генерации списка. Если нет
файлов нужного нам типа, то команда ls возвращает статус, отличный от
нуля, тем самым включается оператор ||. В сущности этот фокус shell
дает нам встроенный оператор if-then, который может использовать результаты
предыдущей команды. Если выполнение команды ls неудачно (т.е.
не найдены подходящие файлы), это сообщение об ошибке отбрасывается
(не выводится) и выполняется оператор echo. Команда echo сообщает, что
нет файлов, чтобы вы знали об этом.
Всякий выход команды ls пропускается через команду sed для отбрасывания
расширения имени файла .time. Для сохранения места и для
удобства пользователя мы хотим только посмотреть и напечатать имена
проектов, а не имена файлов. Однако, мы хотим хранить имена файлов в
специальном внутреннем формате так, чтобы мы могли проще обрабатывать
их и поддерживать уникальные имена.
Команда просмотра выполняется в строках 54-60. Текущий файл проекта
проверяется на то, было ли выбрано имя. Если нет, главное меню
выводится снова. В противном случае печатается строка из черточек, выводится
командой more файл проекта и печатается еще одна строка черточек
для обрамления выведенной информации. Вы можете удивиться, почему
первым символом оператора echo является двоеточие (:). Это некоторый
казус, поскольку, если вы попытаетесь отобразить символ черточки (-) в
качестве первого символа, то оператор echo "подумает", что это пустая
опция и не выведет ее на экран. Вы просто должны поставить в первой
позиции какой-то непустой символ, отличный от черточки.
Включение подсчета времени выполняется в строках 61-70. Текущее
имя проекта проверяется на то, было ли оно выбрано. Если да, то проверяется,
был ли отключен файл проекта. Мы выполняем это, используя команду
tail для выделения последней строки файла, затем передаем по
конвейеру эту строку команде cut, где мы изменяем символ-разделитель
на символ : и отрезаем первое поле. Если в этом поле находятся символы
OFF, все в порядке. После этого строка 69 выводит на экран сообщение
для пользователя, а строка 70 вставляет в файл проекта строку ON, за
которой следует текущая дата. Тем самым файл отмечается как включенный.
Подсчет времени начался. Если эта операция уже была включена, мы
сообщаем об этом пользователю и выходим из данной операции меню.
Строки 71-80 обрабатывают отключение подсчета времени. Здесь почти
все идентично тексту программы, который включает эту операцию,
только если там было слово "on", то здесь слово "off".
Строки 81-101 обрабатывают подчиненное меню выдачи отчетов и статистики.
Как видите, экранное меню спланировано таким же образом: цикл
while, печать меню, чтение ответа, выполнение оператора case в зависимости
от выбранной команды и т.д. Команда r подобна главному меню,
только сокращена для того, чтобы поместиться внутри оператора case. Вы
также можете заметить, что в строках 96-99 выполняется не очень много
обработки. Это то место, где вы должны выполнить некоторую работу.
Строка 102 выполняет обработку ошибок при любом неверном вводе.
Печатается сообщение об ошибке, оператор case выходит на следующую
итерацию цикла while, и все начинается сначала.
МОДИФИКАЦИИ
Основной возможностью для модификации является добавление фактической
обработки информации о времени. Один из подходов к этому - запросить
почасовой тариф времени в момент создания файла проекта (если
считать, что вы работаете на таких основаниях). Этот тариф может храниться
первым пунктом в файле проекта. Следующие две строки могут быть
отведены для "счета по текущему сеансу" и "общего счета" соответственно.
Когда подсчет времени работы над проектом отключается, можно проанализировать
текущее системное время и начальное время подсчета и затем
пересчитать в минуты (для упрощения арифметики) с использованием
команды expr (или, возможно, awk). Затем это значение можно умножить
на хранимый в файле тариф времени, а результат сохранить в записи о
текущем сеансе работы и ДОБАВИТЬ к совокупной общей записи.

ИМЯ: today

today Печать календаря с подсвеченной сегодняшней датой
НАЗНАЧЕНИЕ
Модифицирует вывод утилиты cal для печати сегодняшней даты ин-
версным цветом.
ФОРМАТ ВЫЗОВА
today
ПРИМЕР ВЫЗОВА
today Печатает календарь на этот месяц с подсвеченной
сегодняшней датой
ТЕКСТ ПРОГРАММЫ today
1 :
2 # @(#) today v1.0 Calendar with today highlighted
Author: Russ Sage
2а Календарь с подсветкой сегодняшнего дня
4 SYSV=n
6 set `date`
8 if [ "$SYSV" = "y" ]
9 then RVR=`tput smso`
10 BLNK=`tput blink`
11 NORM=`tput rmso`
12 cal ${2} ${6} | sed "s/${3}/${RVR}${BLNK}${3}${NORM}
/g" -e "s/^/ /"
13 else RVR="^[[7m" # termcap so
14 NORM="^[[0m" # termcap se
15 cal ${2} ${6} | sed -e "s/ ${3}/ ${RVR}${3}${NORM}
/" -e "s/^/ /"
16 fi
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ
RVR Управляющий символ инверсного отображения для
вашего терминала
BLNK Управляющий символ мерцания для вашего терминала,
если таковой имеется
NORM Управляющий символ для возврата терминала в
обычный режим
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН today?
Всегда приятно иметь инструментальные средства, предоставляющие
нам информацию о нашей среде. Тип информации, который мы здесь
рассматриваем - это модифицированный вывод календаря. Стандартная команда
cal выводит дни месяца, но не сообщает вам, какой день сегодня.
Как мы это узнаем? Мы должны запустить команду date для определения
текущего дня. Довольно тривиальная задача, однако наше решение может
дать некоторую ценную графику, которая может добавить действительно
полезную информацию к календарю.
Для выполнения такой модификации нам необходимо выполнить постобработку
результата команды cal. Поскольку не все терминалы обладают
одинаковыми возможностями, эта программа должна быть приспособлена к
вашей машине.
ЧТО ДЕЛАЕТ today?
Today - это постпроцессор для команды cal, который делает ее результат
более информативным и графически наглядным. Выполнение модификации
команды cal зависит от того, в какой системе вы работаете. Если
вы в системе UNIX System V (версия 2 или старше), то у вас есть утилита
terminfo. Terminfo является заменой для файла termcap и поставляется
с несколькими утилитами, которые возвращают значения с информацией
о ПРИЕМЫ ПРОФЕССИОНАЛЬНОЙ РАБОТЫ В UNIX
нальных характеристик. Если ваш компьютер работает не с системой
System V, то вам необходимо немного поисследовать тип ваших конкретных
терминалов и внести полученные значения в вашу программу.
Вся история с утилитами termcap и terminfo иллюстрирует эволюцию
работы в среде UNIX. UNIX с самых первых дней стремился быть независимым
от типов устройств. Первым шагом явилось использование файлов устройств
и драйверов. Следующим шагом был файл termcap, который предоставил
единообразный способ получения информации о терминалах.
Последним этапом является утилита terminfo, предоставляющая эту информацию
таким способом, который лучше обеспечивает функциональный доступ
из программ.
Поскольку в пределах командного файла не так легко определить, с
какой системой работает ваш компьютер, использована переменная SYSV.
Эту переменную можно изменить при помощи редактора, поэтому today может
работать в разных системах. Способами выяснения системы могли бы
служить программа uname, существование определенных shell-программ в
каталоге /bin или какой-нибудь системный файл, содержащий номер
версии. По умолчанию переменная SYSV установлена так, чтобы утилита
today работала НЕ в системе System V. С этим связано существование
фрагмента программы, который нужно изменять вручную.
Как вы можете самостоятельно получить информацию о терминале?
Каждый терминал имеет свои специфические особенности. Все терминалы
характеризуются в основном файлом описания терминала termcap. В этом
файле каждая характеристика дается под своим именем вместе с аппаратным
кодом этой функции. Таким образом, мы можем, например, редактором
vi заглянуть в termcap и увидеть, как управлять терминалом, на котором
мы работаем. Файл termcap сильно зашифрован и загадочен. По данному
вопросу не очень много документации, что порождает множество экспериментов
и ошибок.
Переменными, которые нас интересуют, являются "so" для выделения
информации (инверсный режим) и "se" для завершения выделения (обычный
режим), а также режим мерцания, если он есть у вашего терминала.
Termcap, похоже, не содержит информацию о том, как включить режим мерцания,
поэтому вам, вероятно, нужно будет для этого посмотреть документацию
на терминал. В системе System V (версия 2) команда tput возвращает
соответствующее значение.
По умолчанию в today выполнены установки для ANSI терминала, являющегося
консольным в системе XENIX. Эти коды были вручную извлечены
из файла /etc/termcap и вставлены в текст программы. Если ваши коды
отличаются, вы должны выяснить их. Обратите внимание, что в файле
/etc/termcap символ ESCAPE представлен как \E. Это не годится для
today, и вы должны изменить такое представление на настоящий ESCAPE.
Поскольку ESCAPE является символом выхода из режима ввода в редакторе
vi, вы должны использовать команду control-V в этом редакторе для ввода
управляющих символов. Последовательность control-V вызывает печать
символа ^, а ESCAPE - печать символа [. Таким образом, реальная команда
входа в инверсный режим в редакторе vi представлена последовательностью
^[[7m. Эта команда включает символы ^[ в качестве ESCAPE и затем
обычные символы [7m для изменения режима.
Теперь, когда характеристики терминала учтены, цель утилиты today
- выделить текущий день календаря в инверсном виде, а все остальное
оставить в обычном виде. Это делается путем передачи по конвейеру выхода
команды cal команде sed. Утилита sed находит число в выходных
данных и подставляет специальную графическую ESC-последовательность.
Поскольку ваш терминал использует специальные символы для изменения
режима, вы не увидите их выдачи на экран.
Данная программа не имеет опций или какого-то особого входа. Она
распечатывает календарь с отмеченным сегодняшним днем.
ПОЯСНЕНИЯ
В строке 4 выполняется инициализация переменной SYSV значением
"n". Это заставляет программу переходить к особой области, в которой
жестко закодированы управляющие коды терминала, определенные вручную.
Если вы работаете с последними версиями системы System V, то вам нужно,
чтобы эта переменная имела значение "y".
В строке 6 позиционным параметрам присваивается результат команды
date. Мы обратимся к этим значениям позже.
Строки 8-16 - это остальная часть программы. Они представляют собой
один оператор if-then-else. Строки 9-12 поддерживают принятый в
System V метод tput для получения характеристик терминала, а строки
13-15 управляют ручным способом их получения.
В обоих случаях переменным shell присваиваются ESC-последовательности.
Эти значения используются позже. В обоих случаях выполняется
вызов команды cal с использованием значений месяца и года, полученных
от команды date. Этот образ календаря пропускается по конвейеру через
утилиту sed, которая ищет указанный день "сегодня", также полученный
от команды date. Когда этот день найден, sed заменяет цифры дня на
последовательность включения инверсного режима, последовательность
включения режима мерцания, если он есть, и символы дня, а затем последовательность
возврата в обычный режим работы терминала. Последняя команда
sed заполняет пробелами начало строки для размещения ее в центре
экрана.
УПРАВЛЕНИЕ ДЕЛОПРОИЗВОДСТВОМ
Много рабочего времени тратится на запоминание важной информации,
такой как деловые встречи, адреса, номера телефонов, расписания, учетные
сведения о проектах и так далее. Большинство из такой информации
может быть сохранено в системе UNIX в виде простых структурированных
текстовых файлов, которыми можно манипулировать с помощью соответствующих
средств. Автоматизация этой области может освободить много времени
для выполнения "настоящей" работы.
Хотя мощные коммерческие программы для сохранения такой информации
имеются в среде MS-DOS, эти программы не имеют широкого распространения
в UNIX. Часто у вас нет необходимости в отдельной большой
программе для выполнения таких работ в UNIX. UNIX обеспечивает хороший
компромисс между легкими в применении, но не гибкими коммерческими
программами и программированием на мощных, но непростых в использовании
традиционных языках программирования. Возможности языка shell плюс
богатый ассортимент встроенных команд UNIX дают мощный, гибкий и СРАВНИТЕЛЬНО
простой в использовании компромисс. В данном разделе мы
представляем широкий набор средств управления личной информацией, которые
вы можете приспособить для своих нужд.
Для шутки начнем с программы по имени jargon - генератора технических
терминов, конструирующего замысловатые фразы. С его помощью
можно удивлять людей тем, как много вы знаете, или начать прибыльную
вторую карьеру в качестве составителя рекламы. Комбинирование слов порождает
сотни фраз.
Затем мы рассмотрим программу phone. Phone - это командный файл,
управляемый с помощью меню, который поддерживает базу телефонных номеров
и сопутствующей информации. Она сводит воедино разнообразные
аспекты сопровождения базы данных и обслуживания запросов к ней.
Последнее инструментальное средство называется office. Это управляемая
при помощи меню утилита, которая дает одноклавишный доступ ко
всем функциям делопроизводства. Сюда относятся почта, новости, календарь,
номера телефонов и автоматические напоминания.

ИМЯ: jargon

НАЗНАЧЕНИЕ
Компьютеризованная версия старого настольного генератора техни-
ческих терминов.
ФОРМАТ ВЫЗОВА
jargon
ПРИМЕР ВЫЗОВА
jargon Если ввести 125 в ответ на запрос, то на выходе
125 получится фраза Total Monitored Concept
ТЕКСТ ПРОГРАММЫ jargon
1 :
2 # @(#) jargon v1.0 Technical jargon generator
Author: Russ Sage
2а Генератор технического жаргона
4 echo "\n\t\tThe Jargon Generator"
5 while :
6 do
7 echo "\nEnter a 3 digit number (000-999), ?
, or : \c"
8 read NUM
10 case $NUM in
11 "") exit;;
12 \?) cat " /dev/
null ||
27 { echo "\nNot a valid number, try again
"; continue; };;
28 *) echo "\nInvalid input, try again"
29 continue;;
30 esac
32 N1=`echo $NUM|cut -c1`
33 N2=`echo $NUM|cut -c2`
34 N3=`echo $NUM|cut -c3`
35 SEN=""
37 case $N1 in
38 0) SEN="${SEN}Integrated ";;
39 1) SEN="${SEN}Total ";;
40 2) SEN="${SEN}Systematized ";;
41 3) SEN="${SEN}Parallel ";;
42 4) SEN="${SEN}Functional ";;
43 5) SEN="${SEN}Responsive ";;
44 6) SEN="${SEN}Optional ";;
45 7) SEN="${SEN}Synchronized ";;
46 8) SEN="${SEN}Compatible ";;
47 9) SEN="${SEN}Balanced ";;
48 esac
50 case $N2 in
51 0) SEN="${SEN}Management ";;
52 1) SEN="${SEN}Organizational ";;
53 2) SEN="${SEN}Monitored ";;
54 3) SEN="${SEN}Reciprocal ";;
55 4) SEN="${SEN}Digital ";;
56 5) SEN="${SEN}Logistical ";;
57 6) SEN="${SEN}Transitional ";;
58 7) SEN="${SEN}Incremental ";;
59 8) SEN="${SEN}Operational ";;
60 9) SEN="${SEN}Third-Generation ";;
61 esac
63 case $N3 in
64 0) SEN="${SEN}Options";;
65 1) SEN="${SEN}Flexibility";;
66 2) SEN="${SEN}Capability";;
67 3) SEN="${SEN}Mobility";;
68 4) SEN="${SEN}Programming";;
69 5) SEN="${SEN}Concept";;
70 6) SEN="${SEN}Time-Phase";;
71 7) SEN="${SEN}Projection";;
72 8) SEN="${SEN}Hardware";;
73 9) SEN="${SEN}Contingency";;
74 esac
76 echo "\n\"$SEN\""
77 done
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ
N1 Первая цифра числа
N2 Вторая цифра числа
N3 Третья цифра числа
NUM Число, введенное пользователем с клавиатуры
SEN Предложение, полученное из найденных слов
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН jargon?
В нашем скоростном, заполненном техникой мире на нас оказывается
большое давление с целью получения каких-то результатов. К сожалению,
быстрота современной техники относится к выполнению программ, а не к
их созданию. Когда мы сделали всего лишь третью часть программы, а уже
пора ее предъявлять на рынок - как мы можем создать впечатление, что
наш продукт делает больше, чем на самом деле? Необходимо немного
пустить пыль в глаза заказчику. Пускание пыли состоит из фраз, которые
выглядят и звучат впечатляюще. При строгой проверке мы можем увидеть,
что фраза составлена из настоящих английских слов, значение которых
можно посмотреть в словаре. Эта фраза в целом совершенно бессмысленна,
но если нам повезет, то читатель этого не заметит!
Одним из простых способов получения таких вещей является наличие
таблицы взаимозаменяемых слов, которые можно использовать для составления
предложений. Если у вас система BSD, то вы можете запустить
jargon вместе с программой fortune, которая имитирует случайные подарки
судьбы. Мудрость веков может принадлежать вам!
ЧТО ДЕЛАЕТ jargon?
Jargon - это инструмент для генерации фраз, состоящих из технических
терминов. Строится фраза, которая является комбинацией трех
слов. Первые два слова являются прилагательными, используемыми для
описания третьего слова, имени существительного. Каждое слово выбирается
из столбца, содержащего десять возможных слов. Именно это придает
программе jargon ее творческие способности. Она может объединять слово
из одного столбца с любым словом из другого столбца для построения
многих полезных фраз. Если вы хотите увидеть всю таблицу слов, введите
символ ?. Вот пример таблицы:

|
| The Jargon Generator
|
| Enter a 3 digit number (000-999), ?, or : ?
|
| 0. Integrated 0. Management 0. Options
| 1. Total 1. Organizational 1. Flexibility
| 2. Systematized 2. Monitored 2. Capability
| 3. Parallel 3. Reciprocal 3. Mobility
| 4. Functional 4. Digital 4. Programming
| 5. Responsive 5. Logistical 5. Concept
| 6. Optional 6. Transitional 6. Time-Phase
| 7. Synchronized 7. Incremental 7. Projection
| 8. Compatible 8. Operational 8. Hardware
| 9. Balanced 9. Third-Generation 9. Contingency
|
Имеется три возможных варианта ввода. Вы можете ввести трехзначное
число, знак вопроса для печати таблицы или возврат каретки для выхода
из программы. Проверяется, является ли строка из трех цифр допустимым
числом. Если все в порядке, то введенное число разбивается на
три составляющие его цифры. Каждая цифра используется как ключ поиска
в массиве из десяти слов для получения куска предложения. Затем все
слова объединяются для формирования жаргонной фразы. После этого вам
выдается запрос на дальнейший ввод.
Если вы ввели знак вопроса, печатается таблица слов, как показано
в предыдущем примере, и вам снова выдается запрос. Если был введен
только возврат каретки, то цикл, выдававший запросы, завершается и заканчивается
выполнение программы.
Внутри самой программы выполняется только проверка на ошибки.
Программа jargon организована аналогично программе, управляемой с помощью
меню, однако никакого меню нет. Это просто цикл, который выполняется
до тех пор, пока не будет нажат возврат каретки.
ПРИМЕРЫ
1. $ jargon

898

Открывается секрет, что у меня персональный компьютер 898, то
есть Compatible Third-Generation Hardware (совместимая аппаратура
третьего поколения).
2. $ jargon

187

Оказывается это Total Operational Projection (всеобъемлющий разрабатываемый
проект).
ПОЯСНЕНИЯ
Строка 4 печатает заголовок при начальном запуске программы.
Отображается возврат каретки, две табуляции и сообщение.
Строки 5-77 представляют собой один большой бесконечный цикл
while. В нем имеется всего одна точка выхода, находящаяся внутри оператора
case. Строка 7 выводит приглашение, а строка 8 читает вводимый
ответ в переменную NUM.
Строки 10-30 являются оператором case, который проверяет информацию,
введенную с клавиатуры. Если был введен только возврат каретки,
строка 11 рассматривает это как нулевой ввод. В этом случае выполняется
выход из программы. Это и есть нормальная точка выхода.
Ввод вопросительного знака соответствует строке 12. Обратите внимание,
что знак вопроса экранирован. Это выполнено по той причине, что
символ ? имеет для shell специальное значение. Он используется как
представитель любого одиночного символа при порождении имени файла.
Для того, чтобы сделать знак вопроса обычным символом, мы должны экранировать
его для отмены специального значения.
В строках 12-24 команда cat получает текст из последующего фрагмента
самой программы. Такого рода файл иногда называют "встроенным
документом". Возможность обработки встроенного документа активируется
последовательностью символов "". Слово, которое следует за ней, является
признаком начала-окончания, в данном случае EOF. После того, как
текст будет выведен на экран, строка 25 продолжает выполнение следующей
итерации внешнего цикла while.
Попутно отметим: для того, чтобы увидеть, как shell управляет
встроенными документамм, посмотрите во время работы командного файла
каталог /tmp. В нем находится файл с именем "shXXXX", где XXXX - идентификатор
shell, создавшего этот файл. Весь встроенный документ целиком
помещается в этот временный файл. Затем shell выполняет переадресацию
своего входа на этот временный файл. Довольно простой метод.
Строка 26 соответствует всем случаям ввода, когда имеется три
символа. Эти символы могут быть буквами и/или цифрами. В этом месте
shell еще не знает, есть ли там буквы. Для проверки того, что все введенные
символы являются цифрами, мы должны использовать команду expr,
выполняющую дополнительный анализ. Оператор expr указывает, что нужно
сравнить строку NUM с последовательностью "начало строки, цифра, цифра,
цифра, конец строки". Если сопоставление успешно, expr возвращает
статус успешного возврата и программа идет дальше. Поскольку expr
возвращает число совпавших символов, этот результат должен быть перенаправлен
в каталог /dev/null.
Если сравнение завершилось неудачей, активизируется оператор ||
(мы уже видели такого рода управляющую структуру ранее), который печатает
сообщение об ошибке и вызывает следующую итерацию цикла while.
Такой синтаксис представляет собой то же самое, что и оператор
if-then-else. Поскольку за символами || может следовать список команд,
то внутрь простых разделителей списка {} можно вставить более одной
команды. Будьте внимательны. Если отсутствуют символы-разделители, то
оператор continue выполнится как после сообщения об ошибке, ТАК И при
успешном выполнении команды expr. Это может заставить вас заниматься
отладкой, пока вы не обнаружите, что же произошло на самом деле.
Точно такую же проверку числа можно было бы выполнить с помощью
оператора case. Синтаксис был бы таким же, за исключением зацепочных
символов ^ и $. Шаблон для оператора case выглядел бы так:
[0-9][0-9][0-9] statement;;
Я использовал оператор expr для того, чтобы показать, каким образом
expr может быть использован для выполнения такого рода проверки.
Любой другой ввод перехватывается в строке 28 путем проверки на
совпадение с универсальным символом-заменителем *. Выводится сообщение
об ошибке, и оператор continue вызывает следующую итерацию цикла
while, который запрашивает новый ввод.
Обратите внимание, как shell рассматривает строки. Команда test
фактически выполняет сравнение значения строки. Несмотря на то, что
команде test(1) посвящена своя страница справочного руководства, она
является встроенной функцией shell. Если при вызове команды test
использован синтаксис =, !=, то два аргумента рассматриваются как
строки. Но если в команде test используется синтаксис вида -lt, -eq,
то производится сравнение двух аргументов-строк как чисел и выполняется
их числовая обработка. Эти два различных режима нельзя смешивать,
т.е. нельзя сравнивать строки при помощи числового оператора, например
str1 -eq str2.
В строках 32-34 каждая цифра вырезается из числа и помещается в
свою собственную переменную. Затем эти переменные используются в качестве
индекса в операторе case, который содержит магические слова.
Строка 35 инициализирует переменную SEN для сбора предложения. (Предварительное
замечание перед тем, как мы начнем получать письма от ревнителей
чистоты грамматики - да, мы знаем, что то, что мы генерируем,
является фразой, а не настоящим предложением, поскольку отсутствует
глагол.) Начинаем мы с пустого предложения и добавляем к нему каждый
раз по одному слову.
Строки 37-48 представляют собой первый оператор case. Оператор
case берет значение переменной N1 и добавляет слово с таким номером к
предложению. На самом деле нет необходимости включать значение переменной
SEN в правую часть присваивания, поскольку еще ничего нет. Однако
это делает текст программы более гибким, если мы решим предварительно
сгенерировать первоначальное предложение некоторыми другими
средствами. Аналогичные операторы case обрабатывают две следующие цифры.
Полученное предложение выводится в строке 76 после того, как найдены
все слова. Вы могли бы сказать, что вся эта штука представляет
собой 754 подвиг, или Synchronized Logistical Programming (Синхронизированное
логическое программирование).
МОДИФИКАЦИИ
Можно немного поиграться с этой программой. Вы могли бы получать
случайное число, зависящее от текущего системного времени (используя
извлечение и команду expr) и основывать поиск на этом числе, а не на
числе, введенном пользователем. Вы также могли бы использовать каждую
цифру случайного числа для управления выбором слова из различных баз с
жаргонными терминами, имея, возможно, по одному файлу для каждой из
трех позиций слова и организуя цикл на случайное значение для чтения
каждого слова. Слова могли бы читаться и удаляться из файла до тех
пор, пока не прочитается слово, которое соответствует случайной цифре.
Имеется множество других возможностей. Если вы вовремя не остановитесь,
то обнаружите, что вы написали компьютерную игру!

ИМЯ: phone

phone База данных с телефонными номерами
НАЗНАЧЕНИЕ
Управляемое меню средство, поддерживающее базу данных с телефон-
ными номерами
ФОРМАТ ВЫЗОВА
phone
ПРИМЕР ВЫЗОВА
phone Вызов телефонной базы данных
s Ввод опции поиска
russ Поиск номера телефона Расса
ТЕКСТ ПРОГРАММЫ phone
1 :
2 # @(#) phone v1.0 Maintain telephone database
Author: Russ Sage
2а Сопровождение телефонной базы данных
4 if [ $# -gt 0 ]
5 then echo "phone: argument error" "&2
6 echo "usage: phone" "&2
7 exit 1
8 fi
10 BASE="$HOME/.phone.list"
12 while :
13 do
14 echo "
16 phonebase = $BASE
18 PHONE MENU
19 ----------
20 add name to list
21 delete name from list
22 edit list
23 search for name in list
24 view complete list
25 - exit program
27 Press a,d,e,s,v or : \c"
28 read RSP
30 case $RSP in
31 "") exit 0
32 ;;
33 a|A) echo "\nEnter name to add ( to exit
): \c"
34 read NAME
35 if [ "$NAME" = "" ]
36 then continue
37 fi
38 echo "Enter description of person: \c"
39 read DESC
40 echo "Enter number to add: \c"
41 read NUM
42 echo "$NAME\t$DESC\t\t\t$NUM" "" $BASE
43 sort -t" " +1 -1.3b -o $BASE $BASE
44 ;;
45 d|D) echo "\nEnter name to delete ( to exit
): \c"
46 read NAME
47 if [ "$NAME" = "" ]
48 then continue
49 fi
50 sed -e "/$NAME/d" $BASE.new
51 mv $BASE.new $BASE
52 ;;
53 e|E) vi $BASE
54 ;;
55 s|S) echo "\nEnter name to search: \c"
56 read NAME
57 echo "\n
"
58 grep -y "$NAME" $BASE
59 echo "
"
60 ;;
61 v|V) echo "\n\tPhone List\n\t---------" &
62 more $BASE
63 echo "\nhit \c"
64 read RSP
65 ;;
66 *) echo "Not a valid command"
67 ;;
68 esac
69 done
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ
BASE Фактическое имя файла телефонной базы данных
DESC Описание, вводимое в базу данных
NAME Имя, вводимое в базу данных
NUM Номер телефона, вводимый в базу данных
RSP Ответ пользователя на приглашение
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН phone?
Телефоны представляют собой очень важную часть нашего рабочего
дня. Мы используем их для сообщения своих планов, отдачи распоряжений
и для многих других целей. Немного поразмышляем. Если почти на каждом
рабочем месте в США установлен телефон, то телефонных номеров миллионы.
Каким образом вы храните все ваши телефонные номера?
Нам необходима очередная база данных. Эта база данных должна управлять
вводом данных, их извлечением, модификацией и удалением. У нас
теперь есть опыт в реализации программ, управляемых с помощью меню,
поэтому имеет смысл использовать меню и здесь.
ЧТО ДЕЛАЕТ phone?
Phone - это универсальная утилита для использования и управления
базой данных телефонных номеров. Полностью управляемая при помощи меню,
phone содержит все необходимые функции, связанные с управлением
базой данных: добавление, удаление, просмотр, редактирование и поиск.
Файл, представляющий собой базу данных, размещен в вашем регистрационном
каталоге. Это файл $HOME/.phone.list. Phone использует ваш
регистрационный каталог, так что один и тот же исполняемый модуль работает
для любого пользователя. Независимо от того, где размещается
командный файл phone или кто и откуда его запустил в вашей системе, вы
всегда получите именно ваш файл телефонных номеров, поскольку он подключен
к вашему регистрационному каталогу посредством встроенной переменной
shell с именем HOME.
Имеется всего одно главное меню. Оно выводится на экран, и внизу
появляется приглашение. Большинство вариантов выбора запрашивают дополнительную
информацию. Меню выглядит так:

|
|
| phonebase = /usr/russ/.phone.list
|
| PHONE MENU
| ----------
| add name to list (добавить имя к списку)
| delete name from list (удалить имя из списка)
| edit list (редактировать список)
| search for name in list (поиск имени в списке)
| view complete list (просмотр списка)
| - exit program (выход из программы)
|
| Press a,d,e,s,v or :
|
В верхней строке меню выводится имя телефонной базы данных, которую
вы используете. Если это не ваш файл, вы точно знаете, что произошла
ошибка. Опции выбираются нажатием клавиши, соответствующей первой
букве слова. Если был нажат только возврат каретки, программа завершается.
ПРИМЕРЫ
1. $ phone
a
russ sage
unix master
123-4567
Такая последовательность действий используется для добавления еще
одного номера в базу данных. Вызывайте меню, выбирайте опцию добавления
и в ответ на запросы утилиты phone вводите имя, описание и номер
телефона.
2. $ phone
V
Просмотр всех записей в вашей базе данных.
ПОЯСНЕНИЯ
Строки 4-8 выполняют некоторую проверку на наличие ошибок. Если
вы вызвали phone с какими-либо аргументами, выводится сообщение об
ошибке и программа завершается. Поскольку phone управляется при помощи
меню, она не использует аргументы.
Строка 10 инициализирует переменную BASE именем файла телефонной
базы данных. Это назначение выполняется с помощью переменной HOME для
получения вашего регистрационного каталога.
Строки 12-69 представляют собой один большой бесконечный цикл
while. Вся эта структура подобна структуре других программ, управляемых
с помощью меню, которые мы рассмотрели. Само меню выводится оператором
echo. Текст меню НЕ выделен отступами подобно остальной части
программы, поскольку оператор echo воспринимает этот текст как литеральные
данные, включая пробелы.
Строка 28 читает ответ пользователя, а строки 30-68 представляют
собой оператор case, который проверяет значение ответа. Если был нулевой
ввод, программа завершается.
Если была введена буква a, выбирается опция добавления. (В
действительности каждая опция проверяется и на верхнем, и на нижнем
регистре для обеспечения гибкости). Текст программы, соответствующий
добавлению, запрашивает имя, описание и номер телефона. Если вы оказались
в опции добавления случайно, используйте возможность возврата путем
нажатия возврата каретки в ответ на запрос имени. Однако после того,
как вы ввели имя, выйти уже нельзя. Вам необходимо в этом случае
поместить в базу фиктивную запись или нажать клавишу аварийного завершения
для выхода из программы вообще. Описание может быть произвольной
длины, однако оно не смотрится, если очень длинное. После того как все
поля введены, вся строка помещается в конец базы данных. Отметим, что
добавляются символы табуляции для разбиения записи на поля. После этого
база данных пересортировывается, чтобы новая запись заняла свое
место в алфавитном порядке.
Если была введена буква d, то она распознается в строке 45. Запрашивается
удаляемое имя. Если вводится только возврат каретки, оператор
continue возвращает нас обратно в цикл while и снова выводится
главное меню. Для удаления записи использована команда sed, поэтому
вводимое имя должно указываться точно в таком же виде, как оно хранится
в файле. Результат удаления помещается во временный файл, который
затем в строке 51 переименовывается в исходный файл базы данных.
Одной из модификаций может быть показ пользователю того, что он собирается
удалять, и запрос подтверждения. Если подтверждение получено,
то данные удаляются окончательно. Сейчас сделано так, что вы на самом
деле не знаете, что именно собираетесь удалять. Поэтому необходимо
точно указывать удаляемое имя. Здесь выбран относительно быстрый и не
совсем честный подход. А вообще вы даже могли бы использовать непосредственно
редактор vi для поиска и удаления.
Опция редактирования в строке 53 выполняет только одно - вызывает
редактор vi для работы с базой телефонных номеров. Используя редактор
vi, вы можете вручную сделать файл базы данных таким, как вам нужно.
Зачастую формат входных данных "плывет" из-за разной длины. Все данные
хранятся в свободном формате и поэтому их можно как угодно смещать
вдоль строки.
Когда вы редактируете ваш файл редактором vi, все его команды активны.
Действует даже выход в shell и выполнение обычных действий с
системой. Поэтому не забудьте прекратить работу этого shell'а, иначе
вы будете весьма удивлены в конце рабочего дня при выходе из системы.
Для выхода из редактора и возврата к утилите phone используйте обычные
методы, такие как ZZ, :x и :wq.
Опция поиска в строке 55 запрашивает имя, которое нужно искать,
читает это имя, выводит декоративные элементы до и после данных и
использует утилиту grep для нахождения требуемой записи. Утилита grep
в строке 58 использует команду -y, вызывающую нечувствительность к регистру.
Это означает, что вы можете производить ввод искомого элемента
как на верхнем, так и на нижнем регистре. Если не найдено ничего, то
ничего и не выводится на экран.
Опция просмотра начинается со строки 61. Сначала выводится заголовок
в фоновом режиме. Это делается для быстроты. На самом деле это
просто эксперимент для того, чтобы увидеть, будут ли асинхронные события
производить вывод на экран синхронно. В данном случае это именно
так. Файл выводится командой more. Пока утилита more постранично выводит
файл, все ее команды нам доступны. (Наш командный файл m в главе 4
иллюстрирует, что мы можем делать с помощью more.)
В строке 63 выводится сообщение пользователю о том, что он должен
нажать возврат каретки. Строка 64 выполняет чтение. Эта последовательность
придерживает распечатанный файл данных на экране, чтобы его
строки не пропали при сдвиге вверх. Если бы этих операторов не было,
напечаталось бы главное меню и мы потеряли бы то, что находилось в
верхней части экрана.
Строка 66 выполняет проверку на ошибки для меню в целом. Если вы
ввели данные, которые не соответствуют ни одному из предыдущих шаблонов,
то им сопоставляется шаблон *. В этом случае печатается сообщение
об ошибке, управление переходит из оператора case в конец цикла while
и при следующей итерации этого цикла снова печатается главное меню.

ИМЯ: office

office Делопроизводитель
НАЗНАЧЕНИЕ
Обеспечивает простой доступ ко многим утилитам управления делопроизводством,
которые мы уже рассмотрели.
ФОРМАТ ВЫЗОВА
office
ПРИМЕР ВЫЗОВА
office Проверяет мой почтовый ящик и сообщает, пуст ли он
m
ТЕКСТ ПРОГРАММЫ office
1 :
2 # @(#) office v1.0 Office Manager Author: Russ Sage
2а Делопроизводитель
4 if [ $# -gt 0 ]
5 then echo "office: argument error" "&2
6 echo "usage: office" "&2
7 exit 1
8 fi
10 while :
11 do
12 c
13 set `date`
14 echo "
15 $1, $2 $3 $4
17 Office Menu
18 -----------
19 Mail
20 News
21 Calendar
22 Phone
23 Automatic Reminders
24 Shell Command
25 to exit
27 press m,n,c,p,a,s or : \c"
29 read CMD
31 case $CMD in
32 "") exit;;
33 m|M) if [ -s /usr/spool/mail/$LOGNAME ]
34 then echo
35 ll /usr/spool/mail/$LOGNAME
36 echo "\nWould yuo like to see it (
y/n): \c"
37 read CMD
38 if [ "$CMD" = "y" ]
39 then echo
40 mail
41 fi
42 else echo "\nNo mail today"
43 fi
44 echo "\nhit \c"
45 read CMD;;
46 n|N) PWD=`pwd`
47 cd /usr/news
48 echo "\nThe following files are news item
in /usr/news:\n"
49 lc
50 echo "\nEnter filename or to exit: \c"
51 read NAME
52 while [ "$NAME" != "" ]
53 do
54 if [ -s $NAME ]
55 then echo "\n
"
56 cat $NAME
57 echo "
"
58 else echo "$NAME is not a news
file"
59 fi
60 echo "\nEnter filename or to
exit: \c"
61 read NAME
62 done
63 cd $PWD;;
64 c|C) echo "\n"
65 today
66 if [ -s $HOME/calendar ]
67 then echo "Calendar file:\n`ll $HOME/
calendar`"
68 echo "\nCalendar notifications:"
69 PWD=`pwd`
70 cd $HOME
71 calendar
72 cd $PWD
73 echo "\nCheck your mail for calendar
notifications"
74 else echo "\nYou do not have a calendar
file at $HOME"
75 fi
76 echo "\nhit \c"
77 read CMD;;
78 p|P) phone;;
79 a|A) greet
80 $HB/at 11:45 echo ^GLunch in 15 minutes
81 $HB/at 16:45 echo ^GShift change in 15
minutes
83 echo "\nYou will receive notices at 11:45
& 4:45"
84 echo "\nWould you like to leave some
reminders (y/n): \c"
85 read CMD
86 if [ "$CMD" = "y" ]
87 then echo "\nThe syntax is: at HR:MN
executable_phrase"
88 echo "The time now : `date '+%T'`"
89 echo "\n\ncmd ( to exit): \c"
90 read CMD
91 while [ "$CMD" != "" ]
92 do
93 eval $CMD
94 echo "cmd ( to exit
): \c"
95 read CMD
96 done
97 fi;;
98 s|S) echo "\nenter command: \c"
99 read CMD
100 eval $CMD
101 echo "\nhit \c"
102 read CMD;;
103 *) echo "\nInvalid command"
104 echo "\nhit \c"
105 read CMD;;
106 esac
107 done
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ
CMD Содержит различные команды на разных уровнях меню
HOME Еще одна экспортируемая переменная, взятая из
среды выполнения
LOGNAME Shell-переменная, предварительно экспортированная
NAME Содержит имя файла с новостями
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН office?
Многие инструментальные средства в системе UNIX могут выполнять
полезные функции, относящиеся к делопроизводству. В силу способа организации
UNIX, это отдельные и самостоятельные средства. Для того чтобы
узнать о них, необходимо найти все эти средства в справочных руководствах,
изучить каждое из них и все время помнить, как с ними работать.
Это очень неудобно.
Кроме, того, нам необходимо добавить ряд мощных команд к нашему
UNIX, включая несколько отдельных управляемых с помощью меню утилит.
Каким образом мы можем все это об'единить?
Нам необходим механизм, с помощью которого мы можем получить
доступ ко всем функциям, имеющим отношение к делопроизводству. Если
использовать меню, мы сможем вызывать каждую функцию нажатием одной
клавиши.
ЧТО ДЕЛАЕТ office?
Office представляет собой попытку собрать наиболее часто используемые
функции делопроизводства в один пакет, управляемый с помощью
меню. Тем самым office упрощает вызов этих функций, поскольку вам не
обязательно знать, где эти утилиты размещаются и как к ним обращаться.
Если у вас есть помощник-секретарь без всякого опыта работы в системе
UNIX, вы можете поручить ему выполнение многих из этих функций. Вы можете
добавить новые функции и уровни меню, если это необходимо.
В office имеются как неотъемлемые общие функции делопроизводства,
так и специфические добавки. Данная утилита пытается управлять как
основными функциями, так и кругом задач по вашему вкусу. К базовым
функциям относятся почта, новости, календарь и база телефонных номеров.
Добавляемые особенности включают в себя систему напоминаний,
В строке 71 мы получаем для проверки команду главного меню и ининтерпретатора
shell, не выходя из программы office. Благодаря всем
этим качествам, все находится в одном месте и обеспечивается простота
доступа и сопровождения.
Главное меню содержит все доступные функции. Оно выглядит так:

|
| Fri, Jun 20 16:18:23
|
| Office Menu
| -----------
| Mail (Почта)
| News (Новости)
| Calendar (Календарь)
| Phone (Телефоны)
| Automatic Reminders (Автоматические напоминания)
| Shell Command (Переход в shell)
| to exit (Выход)
|
| press m,n,c,p,a,s or :
|
День и дата в левом верхнем углу получены командой UNIX date.
Каждый раз при выводе меню печатается новое значение времени. Оно
сообщает вам о том, когда вы начали работать с этим меню, и как долго
вы работали.
Первым вариантом выбора является почта. Здесь ваш почтовый файл
проверяется на ненулевой размер. Если размер нулевой (или такой файл
отсутствует), выводится сообщение "no mail today" (сегодня почты нет).
Если для вас есть почта, то информация из файла почтового ящика распечатывается
в длинном формате командой ls и выводится запрос о том, хотите
ли вы прочитать вашу почту. Если вы сказали "yes" (да), выполняется
обычная команда UNIX mail.
Вторым вариантом выбора являются новости. Файлы новостей хранятся
в одном каталоге, где каждая порция новостей представляет собой независимый
файл. Все порции новостей хранятся в специальном каталоге с
именем /usr/news. Office выводит список всех файлов новостей и затем
спрашивает, хотите ли вы просмотреть один из них. Если да, введите имя
файла - и он распечатается командой more. Мы предполагаем, что у вас
есть сетевая связь между пользователями или какое-то другое средство
для получения новостей.
Третий вариант выбора - календарь. Поскольку с календарем связана
разнообразная информация, данная опция подразделяется на различные
функции. Сначала выводится полностью календарь на текущий месяц с выделенной
сегодняшней датой. Это делается с помощью утилиты today,
рассмотренной ранее. Затем выполняется обращение к системной утилите
работы с календарем. Если в вашем регистрационном каталоге есть файл с
именем calendar, то утилита calendar (выполняемая командой cron для
просмотра каждого регистрационного каталога) просматривает записи вашего
календаря. Если какие-либо записи относятся к сегодняшнему или
завтрашнему дню, эта утилита посылает их вам по почте.
Командный файл office использует несколько иной подход. Сначала
ваш файл календаря проверяется на существование и ненулевой размер.
Если файл существует, то он выводится на экран в длинном формате, так
что вы можете увидеть сам файл и информацию, относящуюся к нему. После
этого выполняется функция calendar. При таком вызове на стандартное
устройство вывода печатаются все записи, подходящие по времени. Утилита
calendar, вызываемая командой cron, обеспечивает связь только при
помощи почты. Если календарного файла нет, то выводится сообщение, информирующее
об этом.
Следующим вариантом выбора являются телефоны. Для управления базой
данных телефонных номеров используется утилита phone, рассмотренная
в предыдущем разделе. Поскольку при вызове phone не выполняется
никакой предварительной или последующей обработки данных, вы можете
перечитать этот раздел, чтобы вспомнить, как работает эта утилита.
Пятый вариант выбора - автоматические напоминания. Эта опция
предназначена для того, чтобы помогать вам в слежении за важными моментами
времени в течение дня. Первым делом она приветствует вас в духе
утилиты greet. Затем запускаются в фоновом режиме два командных
файла at. Они срабатывают в 11:45 для объявления о ленче и в 16:45 для
объявления о конце рабочего дня. Если вы хотите запустить некоторые из
ваших собственных командных файлов в фоновом режиме выполнения, ответьте
"yes" (да) на следующий вопрос. Для напоминания выводится синтаксис
утитлиты at, печатается текущее время, и ожидается ваш ввод команд
для at. Красиво и просто в применении! (Если в вашей системе имеется
встроенная возможность at, а вы реализовали нашу версию at под
другим именем или в другом каталоге, проследите за соответствующими
изменениями в тексте командного файла.)
Благодаря способу выполнения введенной вами команды at, вы можете
ввести любую команду, но не саму at. Цикл сделан так, что он принимает
столько команд, сколько вам нужно. Для выхода из цикла введите возврат
каретки.
Последняя опция предназначена для выполнения любой желаемой команды
вне программы office, оставаясь тем не менее в office. Вы даже
можете бы запустить еще один shell вне программы office (введя с клавиатуры
sh), а затем по control-D вернуться в office. Нет почти ничего
невозможного.
Для выхода из office достаточно просто нажать возврат каретки.
Тем самым произойдет выход из бесконечного цикла и возврат к предыдущему
shell.
ПРИМЕРЫ
$ office
s
sh
$
Запуск office и выбор работы с shell. Затем запуск shell-команды
с именем sh, т.е. самого shell. При этом вы запускаете новый shell и
получаете его символ приглашения. По окончании работы с этим shell
введите ^d для выхода из него и возврата к главному меню утилиты
office.
ПОЯСНЕНИЯ
Строки 4-8 выполняют проверку на ошибки. Если вы вызвали office с
какими-либо опциями, то вы создали ситуацию ошибки.
Строки 10-107 - это один большой цикл while. Его структура подобна
ранее рассмотренным утилитам, управляемым с помощью меню. Строка 12
очищает экран командой c (см. главу 7). Если вы не хотите читать с забеганием
вперед и включать в текст эту команду, вы можете пока пропустить
эту строку или, если у вас система BSD, использовать команду
clear. В строке 13 позиционным параметрам присваивается результат команды
date.
Строки 14-27 выводят само меню, а строка 29 читает вводимую пользователем
команду. Строки 31-106 представляют собой оператор case, который
распознает введенную команду и выполняет соответствующие
действия.
Строка 32 выполняет выход из программы office, если был введен
только возврат каретки. Строки 33-45 управляют командой mail. Вначале
с помощью команды test проверяется, имеет ли файл с почтовыми сообщениями
размер больше нуля. Если да, выводится пустая строка и почтовый
файл печатается в длинном формате команды ls. Затем вам задается вопрос
о том, хотите ли вы просмотреть почту. Еслы вы вводите "y", вызывается
команда mail. При любом другом вводе выполняется возврат в
главное меню.
Строки 46-63 обрабатывают команду новостей. Сначала переменная
PWD устанавливается на наш текущий рабочий каталог. Это делается для
того, чтобы при переходе командой cd в какое-то другое место, мы смогли
по окончании обработки вернуться туда, где мы начали работать. Затем
по команде cd мы переходим в каталог /usr/news. Это каталог, в котором
хранятся файлы новостей. В системе Berkeley доступен каталог
/usr/msgs. Этот каталог очень похож на /usr/news и может использоваться
вместо него, если у вас есть такое желание. Выводится в виде
столбцов список файлов из каталога /usr/news (строка 49), и вам задается
вопрос о том, хотите ли вы посмотреть какие-нибудь из этих файлов.
Если вы вводите возврат каретки, команда test воспринимает это
как нулевой ввод и выполняется выход из оператора while.
Цикл while выполняется до тех пор, пока значение переменной NAME
не равно нулю (строка 52). Введенное имя проверяется на существование.
Если такой файл существует, то он распечатывается командой cat, обрамленной
строками из черточек. Если файл не существует, то выводится
сообщение о том, что такого файла новостей нет. Затем вас просят
ввести имя другого файла. Этот процесс продолжается в цикле, пока вы
не введете только возврат каретки. Когда вы выходите из цикла в строке
62, выполняется команда cd для возврата в тот каталог, откуда вы начали
работать. Эта команда cd не оказывает никакого влияния после запуска
утилиты office. Вы должны указать команду cd вашему регистрационному
shell. Никакая другая программа, запущенная из порожденного
shell, не может выполнить для вас команду cd.
Строки 64-77 выполняют функции календаря. Сначала выводятся две
пустые строки. Почему две? Потому что обычно команда echo вставляет
символ новой строки в конце каждой выводимой строки, а выводимой в
данный момент строкой является еще один символ новой строки. Затем в
строке 65 запускается программа today. Она печатает изображение календаря
на месяц. Строка 66 проверяет, имеется ли календарный файл в вашем
регистрационном каталоге. Если да, он выводится в длинном формате
команды ls в строке 67. Печатается также сообщение-заголовок "Calendar
Notifications" (календарные извещения).
Строка 69 запоминает текущий рабочий каталог переменной PWD. Затем
мы переходим командой cd в каталог $HOME, чтобы находиться в том
же каталоге, что и календарный файл. В строке 71 вызывается команда
UNIX calendar. Она просматривает календарный файл и выводит все подходящие
по времени сообщения. По команде cd мы возвращаемся в наш исходный
каталог для того, чтобы восстановить порядок. Строка 73 выводит
сообщение, чтобы вы проверили вашу почту на наличие календарных записей.
Как указывалось ранее, команда calendar работает двумя способами.
Оператор else в строке 74 выводит вам сообщение, если у вас нет
календарного файла. По завершении оператора if, в строках 76 и 77 печатается
сообщение о том, что нужно нажать возврат каретки, и выполняется
оператор read. Это блокирующее чтение, которое означает остановку
и ожидание какого-либо ввода. Это противоположность неблокирующему
чтению, или более широко известной технике чтения с опросом. Опрос
возможен в программах, написанных на Си, но запуск опроса в shell может
потребовать немного больших усилий. Для изучения чтения с опросом
ознакомьтесь с главой, описывающей терминальные устройства.
Строка 78 управляет командой phone. Она вызывает утилиту phone,
которую мы рассмотрели ранее, после чего управление передается в конец
цикла while. Печатается главное меню, и все начинается сначала.
Строки 79-97 управляют автоматическими напоминаниями. Сначала запускается
программа greet. Она была описана ранее в этой главе. Затем
в строках 80 и 81 в режиме фонового выполнения вызываются два командных
файла at, которые выполнятся позже в течение дня. В строке 84 вам
задается вопрос, желаете ли вы оставить какие-то свои напоминания.
Если вы отвечаете "y", выполняются строки 87-96. Если вы не ввели "y",
оператор else не выполняется, поэтому управление передается главному
меню. Вы видите, что эта возможность предполагает однократное использование
в течение дня.
Строки 87-89 выводят некоторую информацию, необходимую для того,
чтобы вы завели для себя напоминания. Выводится синтаксис программы
at, текущая дата (только время) и затем приглашение вводить вашу команду.
Строка 90 читает вашу команду. Строка 91 означает, что если был
введен не только возврат каретки, то нужно выполнить этот цикл. Поэтому
цикл while продолжается, пока вы не нажмете только возврат каретки.
Строка 93 пытается выполнить команду, которую вы ввели. Команда eval
используется для того, чтобы раскрыть значения тех переменных, которые
могли быть не раскрыты ранее. Строка 94 просит ввести следующую команду,
а строка 95 читает ее. Поскольку чтение стоит в самом конце цикла,
оператор while выполняет проверку нового ввода с клавиатуры.
Строки 98-102 управляют выходом в shell. Эта часть программы почти
идентична циклу, использованному для напоминаний. Нам выдается
приглашение на ввод команды, она зачитывается в переменную, затем делается
синтаксический разбор этой переменной с помощью команды eval.
Если вы хотите перейти в shell, выполните команду sh - и она породит
shell. Строки 101 и 102 не дают появиться на экране главному меню до
тех пор, пока вы не нажмете возврат каретки.
Строки 103-105 выполняют проверку на ошибки ввода. Любая неправильная
команда попадает сюда. Выводится сообщение об ошибке и выдается
приглашение "hit " (нажмите возврат каретки).
* ГЛАВА 6. УПРАВЛЕНИЕ ЛИЧНОЙ ИНФОРМАЦИЕЙ II: *
ОСВЕДОМЛЕННОСТЬ ПОЛЬЗОВАТЕЛЯ
И ЗАЩИТА ЛИЧНОЙ ИНФОРМАЦИИ
ОСВЕДОМЛЕННОСТЬ ПОЛЬЗОВАТЕЛЯ
activ показать активность терминалов
info показать информацию о паролях пользователей
uchk посмотреть процессы других пользователей
watch наблюдать за регистрацией в системе
whox команда who с дополнительными возможностями
ЗАЩИТА ЛИЧНОЙ ИНФОРМАЦИИ
acme показать учетную информацию
inuse запретить использование терминала
lock блокировать и разблокировать файлы
УПРАВЛЕНИЕ ЛИЧНОЙ ИНФОРМАЦИЕЙ II:
ОСВЕДОМЛЕННОСТЬ ПОЛЬЗОВАТЕЛЯ
И ЗАЩИТА ЛИЧНОЙ ИНФОРМАЦИИ
ВВЕДЕНИЕ
Мы уже разработали целый ряд инструментальных средств, которые
помогают нам более эффективно управлять нашей личной информацией. Однако
мы работаем не в вакууме. Каждый из нас работает с другими людьми
и обычно использует систему вместе с другими пользователями. По
нескольким причинам важно иметь представление о том, что делают в
системе другие люди. Во-первых, у нас может возникнуть необходимость
наметить работу, интенсивно использующую процессор, на то время, когда
загрузка системы невелика. Изучение типичных вариантов загрузки системы
может помочь нам спланировать такую работу и сотрудничать с другими
пользователями со взаимной выгодой.
Нам также может понадобиться знать, находится ли кто-то на своем
рабочем месте, чтобы мы могли поговорить с ним, или знать, что кто-то
очень занят и был бы недоволен, если его прервут.
Наконец, существуют вопросы защиты информации, которым мы уделим
больше внимания в последующих главах. Основой защиты информации является
осведомленность о том, что делают другие - и, возможно, о том,
что они не должны делать.
Поэтому мы здесь представим две группы инструментальных средств.
Средства ОСВЕДОМЛЕННОСТИ ПОЛЬЗОВАТЕЛЯ помогают нам следить за тем, что
делают другие, и тем самым упростить связь с ними. Средства ЛИЧНОЙ ЗАЩИТЫ
важны для защиты нашей учетной информации и наших данных и для
получения информации о наших собственных действиях.
Первая группа рассматриваемых здесь средств, относится к "осведомленности
пользователя". Этими средствами являются activ, info,
uchk, watch и whox.
Командный файл activ представляет собой модификацию команды who,
показывающую имя каждого пользователя, номер терминала и сведения о
том, насколько давно данное лицо вводило что= либо с клавиатуры. По
этой информации мы можем сказать, кто еще работает в системе, активно
ли они работают и, если они используют закрепленные линии, то где они
находятся.
Следующее средство - info. Оно предоставляет возможность получить
информацию о паролях всех указанных в списке пользователей. С помощью
этой утилиты вы можете получить общую информацию об этих пользователях
и увидеть, где размещаются их регистрационные каталоги.
Еще одно информативное средство - uchk. Этот командный файл полезен
для выяснения того, что делает указанный пользователь в настоящее
время. По этой информации вы можете сказать, простаивают ли они или же
делают что-то важное и их нельзя прерывать.
Следующее средство - watch. Эта программа похожа на "демон", который
выполняется в фоновом режиме и постоянно следит за регистрацией
определенных пользователей. Когда нужное вам лицо регистрируется, на
ваш терминал посылается сообщение о том, что данный пользователь вошел
в систему. Таким образом, вы можете продолжать работу вместо того,
чтобы постоянно прерывать ее.
Последняя утилита в данном разделе - это whox. Whox дает вам
исчерпывающие сведения обо всех пользователях, зарегистрировавшихся на
машине. Вы можете получить информацию, похожую на результат команды
who, которая отсортирована различным образом, отобразить информацию о
терминалах или даже передать почтовое сообщение любому пользователю.
ОСВЕДОМЛЕННОСТЬ ПОЛЬЗОВАТЕЛЯ

ИМЯ: activ

activ Показывает активность терминалов
НАЗНАЧЕНИЕ
Показывает имена всех зарегистрированных пользователей, номера их
терминалов и время, когда они последний раз работали с клавиатурой.
ФОРМАТ ВЫЗОВА
activ
ПРИМЕР ВЫЗОВА
activ Выводит на экран информацию об активности
пользователей на своих терминалах
ТЕКСТ ПРОГРАММЫ
1 :
2 # @(#) activ v1.0 Display terminal activity Author: Russ Sage
2а Показать активность терминалов
4 if [ $# -gt 0 ]
5 then echo "activ: argument error" "&2
6 echo "usage: activ" "&2
7 exit 1
8 fi
10 who -u | cut -c1-17,38-42
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ activ?
Первоначально компьютеры использовались исключительно для запуска
замкнутых, независимых задач обработки информации. Появление многопользовательских
систем сделало возможным по крайней мере элементарное
общение пользователей между собой. Такое общение в настоящее время является
аспектом возрастающей важности при работе с компьютером.
Система UNIX предоставляет команды mail и write для пакетного и
интерактивного диалога соответственно. С помощью телефона мы не можем
сказать, доступен ли кто-либо для общения с нами, пока не позвоним и
не поговорим, возможно, с одним или несколькими другими людьми.
Поскольку система UNIX постоянно записывает информацию о деятельности
пользователей, было бы полезным уметь использовать эту информацию,
чтобы узнать, кто доступен для общения, и получить общее представление
о том, чем они занимаются. После этого мы можем решать, как и когда мы
хотим связаться с ними.
Activ может рассказать нам о том, сколько времени прошло с тех
пор, когда кто-либо нажимал клавиши на своей клавиатуре. Если в вашей
машине есть выделенные линии, то вы также можете узнать, находится ли
данное лицо на своем рабочем месте.
ЧТО ДЕЛАЕТ activ?
Activ печатает информацию об активности всех пользователей системы.
Распечатка содержит имя пользователя, номер терминала, на котором
он зарегистрировался, и время последнего обращения к клавиатуре. Если
клавиатура была использована в течение последней минуты, этот терминал
отмечается точкой. Если терминал не был использован в течение последних
24 часов, напротив него печатается слово "old". Во всех остальных
случаях выводится время последнего использования терминала в минутах и
секундах. Вот пример такой распечатки:

|
| root tty01 01:23
| sage tty05 .
| batch tty12 old
| nuucp tty16 .
|
Важным вопросом является то, как система узнает, когда кто-либо
работает с клавиатурой? Поскольку терминальные устройства являются
файлами, мы можем собрать информацию о терминале путем просмотра
состояния файла, связанного с ним. В данном случае искомая информация
- время последнего использования клавиатуры.
То, что мы ищем, уже есть в системе. Это опция -u команды who.
Эта опция, однако, существует только в системе AT&T UNIX System V, поэтому
команда activ, по крайней мере в представленном виде, может быть
реализована только в этой системе. Activ берет выход команды "who -u"
и "отрезает" некоторые данные, не имеющие отношения к нашей цели.
Путем проб и ошибок, а затем проверки исходного кода я обнаружил,
что значение, используемое в команде "who -u", является временем "модификации"
файла устройства, которое сообщает системный вызов stat(2).
На страницах руководства по stat дается полная таблица системных вызовов,
которые изменяют временные характеристики файлов. В табл. 6.1
представлены эти характеристики:
Таблица 6.1
Временные характеристики файла

Временные Системные вызовы, изменяющие временные
характеристики характеристики

Время доступа creat, mknod, pipe, utime, read
Время модификации creat, mknod, pipe, utime, write
Время создания creat, mknod, pipe, utime, write,
chmod, chown, link

Давайте на некоторое время отвлечемся и поговорим о том, что эти
временные характеристики означают и как они могут быть модифицированы.
Одно интересное замечание: UNIX дает вам как обычному пользователю
возможность установить любое время доступа и время модификации файла
по вашему желанию. Это и хорошо, и плохо. Это обеспечивает гибкость,
когда дело касается информации индексного дескриптора файла, но также
позволяет скрывать следы при нарушении защищенной информации. Возьмем,
например, файл login. Кто-нибудь мог бы вставить новую строку регистрации
перед старой и затем вернуть время доступа и время модификации в
исходные значения. Тогда бы никто, просматривая файл /bin/login командой
ls, не смог бы сказать, что файл был изменен.
Однако в системе UNIX имеется третье время - время создания.
Система не позволяет вам изменить это время при помощи обычных команд.
Системный вызов utime(2) обеспечивает изменение только времени доступа
и модификации. Команда touch(1) также может изменить время доступа и
модификации, но не время создания. Команда touch строится только на
системном вызове. Она может делать не больше того, что обеспечивает
системный вызов. Команда fsdb(1) (отладчик файловой системы) является
единственным способом преодоления защиты от изменения времени создания.
Но даже fsdb не может управлять временем создания. Вы должны выйти
за пределы поддерживаемых fsdb требований безопасности. Как это
сделать, показано в последующих главах.
В случае изменения регистрационной записи, рассмотренном выше, вы
могли бы посмотреть на время создания и увидеть, что оно очень близко
к текущему времени. Если же кто-либо использовал fsdb для подделки
времени создания, вы никогда не будете иметь уверенности в том, что
файл не был изменен.
Какое же все это имеет отношение к определению того, делал ли
пользователь что-либо недавно за своим терминалом? Время модификации
изменяется вызовом write(2). Поэтому представляется разумным, что запись
на терминал будет иметь место, когда драйвер читает символ, а затем
посылает его обратно на экран. По мере того, как кто-то нажимает
на клавиши, время модификации постоянно обновляется при эхо-отображении
символов. Когда символы перестают поступать с клавиатуры, приостанавливается
запись в файл терминала. Формула для определения последнего
времени работы за терминалом (last_activity) такова:
last_activity = time(NULL) - mod_time,
где mod_time - время модификации. Эта формула закодирована внутри команды
who(1). Отметим, что вызов команды time со значением NULL возвращает
истинное текущее время.
Команда activ не имеет опций. Если вы используете опции, напечатается
сообщение об ошибке.
ПОЯСНЕНИЯ
В строках 4-8 выполняется проверка на наличие ошибок. Если число
аргументов командной строки больше нуля, на стандартное устройство регистрации
ошибок выводится сообщение и программа завершается с неудачным
статусом.
Строка 10 - это команда, выполняющая вывод. Команда who вызвана с
опцией -u для получения основных данных. Затем ее выход по конвейеру
передается команде cut, которая отображает колонки 1-17 и 38-42. Тем
самым печатается только три поля, как показано в нашем предыдущем примере.

ИМЯ: info

info Вывод на экран информации о пароле пользователя
НАЗНАЧЕНИЕ
Печатает информацию поля комментария из файла /etc/ passwd для
указанного пользователя.
ФОРМАТ ВЫЗОВА
info login_name [ login_name ... ]
ПРИМЕР ВЫЗОВА
info russ Печатает информацию, которая хранится
о пользователе russ
ТЕКСТ ПРОГРАММЫ
1 :
2 # @(#) info v1.0 Display password info on a user Author: Russ Sage
2а Отобразить парольную информацию пользователя
4 for NAME in $@
5 do
6 USER=`grep "^${NAME}:" /etc/passwd`
7 echo "$NAME:\t`echo ${USER}|cut -d: -f6`\t`
echo ${USER}|cut -d: -f5`"
8 done
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ
NAME Каждое имя, указанное в командной строке
USER Полная запись в файле /etc/passwd для данного имени
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ info?
Система UNIX использует конфигурационные файлы для хранения
основной информации о пользователях и другой системной информации. Одними
из наиболее популярных конфигурационных файлов являются
/etc/group, /etc/passwd и /etc/inittab. Для получения информации о
пользователях нам необходимо заглянуть в эти файлы.
В системе Berkeley имеется команда finger, которая получает информацию
о пользователях из их регистрационных каталогов и из файла
паролей. В System V не так много программ, выполняющих такого рода работу,
поэтому нам необходимо разработать их.
ЧТО ДЕЛАЕТ info?
Info - это командный файл, который получает информацию из регистрационного
каталога и комментарий о пользователе из файла /etc/passwd.
Результат выглядит так:

|
| name: home dir comments
| имя: регистрационный каталог комментарии
|
Если в вашей системе используется это поле комментариев, то эта
информация может быть полезной. "Вручную" выполнить просмотр пользователей
в файле паролей можно следующим образом:
grep login_name /etc/passwd
При этом распечатывается вся строка со всеми полями данных. Info
берет эти необработанные данные и выделяет из них каталог регистрации
пользователя и поле комментария.
В командной строке можно указать несколько регистрационных имен.
Каждое имя берется по порядку из командной строки.
ПРИМЕР
$ for NAME in `cat /etc/passwd | cut -d: -f1`
" do
" NAMELIST="$NAMELIST $NAME"
" done; info $NAMELIST
Имя каждого пользователя в файле паролей добавляется к списку
имен. Затем этот список передается в командную строку info, которая
печатает все данные. Смысл команды: "Дай мне информацию обо всех пользователях
системы".
ПОЯСНЕНИЯ
Строки 4-8 - это цикл for, который обрабатывает все имена, переданные
в командной строке. Для каждого переданного имени выполняются
строки 6 и 7.
В строке 6 в переменную USER заносится результат команды grep,
заключенной между символами ударения (`). Начиная с начала строки
(обозначено символом ^), grep ищет имя, за которым следует символ двоеточия
(:). Такое указание заставляет выполнять поиск образца только в
первом поле файла паролей.
В строке 7 мы отображаем нашу выходную строку командой echo,
внутри которой вложены другие команды echo. Мы могли бы получать элементы
информации отдельно, присваивая их значения отдельным переменным,
и затем создавать необходимый формат вывода, используя значения
этих переменных. Однако помещение всей информации в командную строку
работает быстрее, и текст программы более компактный, хотя и не очень
читабельный. Мы можем также использовать символы форматирования в команде
echo для форматирования нашей распечатки.
Сначала отображается имя в том же виде, как оно получено из командной
строки. Затем выводится табуляция (\t). За первой табуляцией
следует поле номер шесть из файла паролей. Поскольку мы еще не имеем
этих данных, мы должны выделить их из значения переменной USER, которую
мы уже имеем из предыдущей строки. Чтобы сделать это, мы командой
echo выводим всю строку и выделяем шестое поле, используя разделяющие
двоеточия. После этого поля мы выводим еще одну табуляцию и затем пятое
поле файла паролей. Мы получили это поле таким же образом, как и
шестое поле - эхо-отображением и выделением.
Такая техника получения данных медленная, поскольку вовлечены все
процессы, но это самый быстрый путь сделать это на языке shell. Команда
awk была бы понятнее и, возможно, быстрее, но наша реализация демонстрирует
гибкость языка shell. Язык shell может выполнять почти
все, но не всегда лучшим образом. Вот почему в некоторых случаях мы
используем язык Си, в чем вы убедитесь по мере продвижения по нашей
книге.

ИМЯ: uchk

uchk Проверка процессов, запущенных другим пользователем
НАЗНАЧЕНИЕ
Отобразить все процессы каждого пользователя, указанного в командной
строке
ФОРМАТ ВЫЗОВА
uchk [-a] login_name [...]
ПРИМЕР ВЫЗОВА
uchk -a Вывод всех процессов, запущенных администраторами
ТЕКСТ ПРОГРАММЫ
1 :
2 # @(#) uchk v1.0 Check processes of another user Author: Russ Sage
2а Проверка процессов другого пользователя
4 trap "rm /tmp/ps$$ 2" /dev/null" 0 1 2 3 15
6 if [ $# -eq 0 ]
7 then echo "uchk: argument error" "&2
8 echo "usage: uchk [-a] login_name [ ... ]" "&2
9 exit 1
10 fi
12 if [ "`echo $1 | cut -c1`" = "-" -a "$1" != "-a" ]
13 then echo "uchk: invalid argument $1" "&2
14 echo "usage: uchk [-a] login_name [ ... ]" "&2
15 exit 1
16 fi
18 ADMIN="administrators names go here"
19 if [ "$1" = "-a" ]
20 then shift
21 set $ADMIN $@
22 fi
24 ps -ef " /tmp/ps$$
25 for NAME
26 do
27 echo
28 fgrep "$NAME" /tmp/ps$$
29 done
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ
ADMIN Строка с именами пользователей, являющихся
администраторами в вашей системе
NAME Содержит каждое имя, считываемое из командной строки
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ uchk?
Поскольку UNIX является многопользовательской системой, множество
задач выполняются одновременно. Единственный способ следить за такими
задачами - с помощью команды ps. Ps - это довольно специфичная команда
в том смысле, что она должна обращаться к памяти (/dev/mem) и проходить
по связанному списку структур процесса. Это тяжелая вещь, поэтому
нелегко сделать свою собственную команду для вывода такого рода информации.
Вдобавок память защищена, и доступ к ней имеет только пользователь
root.
Так что мы должны или удовлетвориться опциями, которые дает команда
ps, или модифицировать вывод этой команды. Uchk скорее относится
к последней категории. Мне хотелось получить список состояний процессов
по имени пользователя, а не смесь информации, относящейся к
разным пользователям. Хотя то, что я хотел, не соответствует ни одной
из стандартных опций команды ps, я смог модифицировать ее выход для
получения того, что мне надо.
ЧТО ДЕЛАЕТ uchk?
Uchk - это средство, которое генерирует, анализирует и сообщает о
процессах, запущенных каждым указанным пользователем. За один раз вы
можете проверить столько пользователей, сколько вам нужно. Все процессы
для каждого пользователя печатаются вместе.
Чтобы делать это, uchk должен работать с временными файлами.
Система UNIX весьма элегантно управляет временными файлами. Shell позволяет
использовать идентификатор процесса для создания уникальных
имен файлов для каждого запуска. После того как утилита запущена, временные
файлы очищаются путем использования команды trap в shell'е. Команда
trap удаляет файл, если что-либо прервало выполнение командного
файла или когда программа завершается. Это прекрасная особенность, которая
исключает накопление случайных файлов в системе.
Если uchk была вызвана без аргументов или была использована недопустимая
опция, то печатается сообщение об ошибке и выполнение завершается.
Uchk имеет переменную ADMIN, которая определяет всех администраторов
вашей системы. Отредактируйте символьную строку, присваиваемую
переменной ADMIN, указав имена администраторов, которых вы хотите проверить.
Имена должны быть разделены пробелами. Это позволит вам проверять
процессы ваших администраторов, указывая опцию -a в командной
строке. Все остальные имена должны быть указаны в командной строке.
Естественно, вы таким же образом могли бы установить другие группы
пользователей. Слежение за администраторами поможет вам оценить, как
они управляют сохранностью информации, а также распознать административные
задачи, которые имеют склонность загружать процессор.
ПРИМЕР
$ uchk -a russ uucp
Показывает процессы всех администраторов, мои собственные, и процессы
uucp по порядку. Все сообщения об ошибках выводятся на стандартное
устройство регистрации ошибок, а списки процессов выводятся на
стандартное устройство вывода.
ПОЯСНЕНИЯ
Строка 4 - это оператор trap. Символьная строка между двойными
кавычками содержит команды, которые должны быть выполнены, когда происходит
прерывание. В этом случае мы удаляем временный файл и переадресовываем
все выходные данные на нулевое устройство. Когда команда rm
пытается удалить несуществующий файл, выводятся сообщения об ошибках.
Поскольку мы не знаем, какие файлы могут быть в наличии в тот момент,
когда возникнет прерывание, то мы хотим избавиться от сообщений об
ошибках. Оператор trap активизируется по выходу из программы (program
exit, сигнал 0), разрыву линии (hangup, сигнал 1), прерыванию
(interrupt, сигнал 2), выходу (quit, сигнал 3) или программному завершению
(software termination, сигнал 15).
В строках 6-10 проверяется, переданы ли какие-либо аргументы.
Если вы вызвали uchk без аргументов, выводится сообщение об ошибке и
uchk завершается.
В строках 12-16 проверяется, указана ли какая-то опция со знаком
минус и является ли она опцией -a - единственно допустимой опцией. Командный
файл делает это, применяя команды проверки для сравнения двух
различных случаев. Первая проверка вырезает первый символ первого позиционного
параметра и смотрит, является ли он символом "-". Следующая
проверка делается для того, чтобы увидеть, что первый позиционный параметр
не является опцией -a. Поскольку обе проверки соединены операцией
AND, то для получения значения "истина" они обе должны быть
истинными. Если они обе истинны, выводится сообщение об ошибке и uchk
завершается.
Почему мы должны выполнять такую сложную проверку? Проблема в
том, что у нас нет иного способа определить, является первый позиционный
параметр опцией или нет. Он может быть опцией, а может быть именем,
которое нужно искать. Таким образом, мы должны задать вопрос:
"Является ли это опцией, и если да, то допустима ли эта опция?"
В строке 18 переменная ADMIN инициализируется символьной строкой,
следующей за ней. Это строка, которую вы должны модифицировать в соответствии
с вашей системой. Все, что вы должны сделать - это использовать
редактор vi и вставить в эту строку имена администраторов, разделенные
пробелами. Ниже мы используем переменную ADMIN для обработки
аргументов командной строки.
В строках 19-22 проверяется, была ли указана в командной строке
опция -a. Если да, эта опция удаляется из командной строки с целью избавления
от нее. Строка 21 использует команду set для того, чтобы поместить
символьную строку ADMIN в позиционные параметры. Команда set
вставляет значение переменной ADMIN, начиная с первого позиционного
параметра, и сдвигает все настоящие параметры вправо. Тем самым в цикл
for передается множество имен, которые должны быть обработаны.
В строке 24 выполняется команда ps для всех пользователей. Эта
команда использует опцию f для вывода большого количества данных. Результат
помещается во временный файл, в имени которого применяется
идентификационный номер процесса. Этот один большой файл представляет
собой источник для остальных распечаток. Возможно, все это немного
отстает от реального времени, но программа выполняется гораздо быстрее,
чем при многократном вызове команды ps.
Строки 25-29 представляют собой цикл for, который выполняется от
$1 до $x, где x - последний позиционный параметр. Для каждого имени
выполняется следующее: печатается пустая строка для разделения листинга
(строка 27), затем командой fgreps (для ускорения) выбираются из
временного файла все процессы, принадлежащие данному пользователю.
Благодаря применению команды fgrep для каждого имени пользователя, все
процессы данного пользователя печатаются за один раз. Когда закончится
проверка всех имен, указанных в командной строке, цикл завершится, завершится
командный файл и сработает оператор trap, который удаляет временный
файл.

ИМЯ: watch

watch Наблюдение за регистрацией указанных пользователей
НАЗНАЧЕНИЕ
Следит за тем, кто работает в системе, и сообщает
о регистрации указанных пользователей.
ФОРМАТ ВЫЗОВА
watch [-k] [login_name ...]
ПРИМЕР ВЫЗОВА
watch Наблюдение за регистрацией всех пользователей,
указанных во внутренней переменной LIST
ТЕКСТ ПРОГРАММЫ
1 :
2 # @(#) watch v1.0 Watch for specific logins Author: Russ Sage
2а Наблюдение за регистрацией пользователей
4 if [ "`echo $1 | cut -c1`" = "=" -a "$1" != "-k" ]
5 then echo "watch: invalid argument $1" "&2
6 echo "usage: watch [-k] [login_name ...]" "&2
7 echo " -k kill background process"
8 exit 1
9 fi
11 if [ "$1" = "-k" ]
12 then if [ -s $HOME/.watch ]
13 then echo "killed `cat $HOME/.watch`"
14 kill `cat $HOME/.watch`
15 rm $HOME/.watch
16 exit 0
17 fi
18 fi
20 echo $$ " $HOME/.watch
22 LIST="root sys bin administrator1 administrator2 $*"
24 while :
25 do
26 for NAME in `who | cut -d" " -f1`
27 do
28 for PERSON in $LIST
29 do
30 if [ "$NAME" = $PERSON" ]
31 then echo ONLINE: $NAME
32 fi
33 done
34 done
35 sleep 10
36 done &
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ
HOME Полный маршрут к вашему регистрационному каталогу
LIST Список имен пользователей системы, разделенных
пробелами
NAME Содержит имена зарегистрированных в настоящий
момент пользователей
PERSON Отдельное имя из списка имен в переменной LIST
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ watch?
В течение рабочего дня множество людей входят в систему и выходят
из нее. Иногда единственный способ связаться с человеком - через машину.
Нам нужно средство, которое автоматически сообщит нам, что нужное
лицо на связи.
ЧТО ДЕЛАЕТ watch?
Подразумевается, что watch является фоновой задачей, которая
постоянно следит за тем, кто зарегистрировался. Когда лицо или лица,
предварительно вами отмеченные, регистрируются в системе, на ваш экран
выводится сообщение о том, что они на связи.
Количество имен, за которыми вы можете следить, не ограничено.
Общий перечень имен - это объединение имен, указанных в командной
строке, и списка системных пользователей, т.е. пользователя root и
всех сопровождающих его администраторов. В командном файле watch
список системных имен ВСЕГДА включен. Это вызвано тем, что это важные
пользователи и вы хотите всегда знать об их входе в систему и выходе
из нее. Это отличает watch от uchk, поскольку последний требует указания
опции -a для включения списка с системными именами.
Хотя мы устанавливаем watch с точки зрения пользователя, который
желает следить за администраторами и другими пользователями, администраторы
тоже могут иметь список "критических пользователей" или предполагаемых
нарушителей защиты информации и использовать watch для получения
сигнала о регистрации таких пользователей.
За кем бы мы ни следили, мы сталкиваемся с одной проблемой. После
регистрации в системе указанного лица сообщение об этом поступает на
ваш экран, не обращая внимания на то, чем вы в данное время занимались,
что не очень приятно. Единственный способ остановить вывод сообщения
- аварийно завершить watch командой kill. Это легко сделать путем
помещения идентификатора этого процесса в файл $HOME/.watch. Затем
этот номер может быть использован в операторе kill для остановки выполнения
командного файла. Для того чтобы попроще избавиться от watch,
возможность аварийного завершения оформлена в виде опции -k данного
командного файла.
ПРИМЕРЫ
1. $ LIST="root bin" watch daemon
Если переменная LIST не была инициализирована внутри самого командного
файла watch, то мы можем инициализировать ее на командном
уровне shell'а, а затем вызвать watch. Список пользователей для watch
будет такой: root, bin и daemon по порядку, поскольку watch добавляет
имена, указанные в ее командной строке к именам, имеющимся в переменной
LIST. Такой способ указания имен более гибок, чем жесткое программирование
части списка в тексте командного файла watch, но он требует
помнить больше информации и больше нужно набирать на клавиатуре.
2. echo "Watch (y/n): \c"
read ANS
if [ "$ANS" = "y" ]
then watch
fi
Это фрагмент, который вы можете вставить в ваш .profile. Когда вы
регистрируетесь, вам автоматически предлагается запустить watch. Если
вы ответите "y", watch станет фоновой задачей и запустится по умолчанию
(просматривая пользователей согласно списку в переменной LIST).
Если будет введен любой символ, отличный от y, watch не запустится.
ПОЯСНЕНИЯ
Строки 4-9 выполняют проверку на наличие ошибок в опциях командной
строки. Для первого позиционного параметра проверяется, что он
имеет тире и не является единственной допустимой опцией "-k". Если результатом
проверки является истина, печатается сообщение об ошибке и
командный файл завершается.
Строки 11-16 проверяют, что первый позиционный параметр - это -k.
Если это так, значит пользователь хочет уничтожить уже запущенный процесс
watch. В этом случае выводится сообщение, указывающее идентификатор
процесса, который будет уничтожен, и выполнение продолжается. В
строке 12 мы смотрим, существует ли в нашем регистрационном каталоге
файл с именем .watch. Если нет, то это означает, что предыдущий экземпляр
watch предположительно уже уничтожен и нет необходимости пытаться
сделать это снова, поэтому происходит переход к оставшейся
части программы и выполнение watch происходит так, как будто опция
"-k" не была указана.
Если же файл .watch имеется, то в строке 14 используется команда
kill для уничтожения фонового процесса watch. Напомним, что при
использовании опции -k мы подразумеваем, что watch был вызван ранее,
поэтому файл $HOME/.watch имеет идентификационный номер самого процесса
watch. В строке 15 удаляется временный файл watch и в строке 16
происходит выход из программы. Теперь командный файл watch более не
выполняется как фоновый.
Строка 20 выполняется, если опция -k не была указана или если нет
файла .watch. (Последнее может произойти, если пользователь пытается
уничтожить процесс, забыв, что он уже был уничтожен.) Если опция -k не
была указана, мы можем считать, что watch был вызван, чтобы стать фоновым
процессом и выполнять свою работу. Для того чтобы сделать это,
текущий процесс отображает свой идентификатор процесса в файл .watch.
Этот файл остается в вашем регистрационном каталоге до тех пор, пока
он не будет удален вручную или же изменен путем повторного запуска
watch.
В строке 22 инициализируется переменная LIST. Ее значением является
символьная строка с именами, разделенными пробелами. Вам нужно
вручную отредактировать переменную LIST перед запуском в вашей системе.
Просто уберите ее нынешнее содержимое и вставьте туда имена администраторов
вашей системы. Если в командной строке будут указаны дополнительные
имена пользователей, они будут добавлены в переменную
LIST посредством символов расширения параметров $*. Тем самым переменная
LIST станет основным списком всех имен пользователей, за которыми
будет вестись наблюдение.
Строки 24-36 выполняют цикл постоянного наблюдения. В начале каждой
итерации с помощью команды who создается список имен пользователей,
который передается циклу for в строке 26. Цикл for использует командную
подстановку для получения списка слов, образованного из первого
поля команды who. Каждое зарегистрированное имя сравнивается со
списком предварительно определенных имен, за которыми мы наблюдаем.
Обратите внимание, что внешний цикл while сам себя помещает на выполнение
в фоновом режиме. Это означает, что вам нет необходимости вводить
это с клавиатуры.
Строки 29-33 управляют внутренним циклом, который проходит по
именам, содержащимся в нашем основном списке, и сравнивает их с именами,
полученными от команды who. Когда имя, полученное от команды who
(имя зарегистрированного пользователя) совпадает с именем в нашем
списке, на экран выводится сообщение о том, что данное лицо зарегистрировалось.
После того как все имена проверены, командный файл watch приостанавливается
на 10 секунд (строка 35). Когда он снова пробуждается, выполняется
следующая итерация вечного цикла while. Все зарегистрированные
имена вновь сравниваются со списком. Это будет продолжаться до тех
пор, пока вы не прекратите выполнение watch. Как отмечалось ранее,
watch можно легко уничтожить с помощью опции -k или же вручную путем
ввода команды "kill `cat $HOME/.watch`".
МОДИФИКАЦИИ
Watch выполняет довольно мало работы и использует какую-то часть
времени центрального процессора. Вы можете поэкспериментировать с увеличением
интервала паузы (sleep), чтобы watch запускался не так часто.
Большинство пользователей находятся в системе по крайней мере минуту,
поэтому вы можете попробовать значение sleep(60). Вы по-прежнему можете
обнаружить регистрацию всех интересующих вас пользователей?

ИМЯ: whox

whox Команда who с дополнительными возможностями
НАЗНАЧЕНИЕ
Предоставляет много дополнений к выходу команды who и позволяет
применять данные who для других приложений.
ФОРМАТ ВЫЗОВА
whox [-f] [-n] [-m] [-p] [-t] [-w] [-x]
где
-f указывает каждого зарегистрированного пользователя
-n сортирует выход команды who по именам
-m передает почту каждому пользователю
-p выводит информацию о паролях пользователей
-t сортирует выход команды who по времени (умолчание)
-w показывает возможность записи на зарегистрированные
терминальные устройства
-x дополнительная информация о регистрационном
каталоге и паролях
ПРИМЕР ВЫЗОВА
whox -w
Показывает права доступа к файлу (возможность чтения и записи)
для каждого зарегистрированного терминального устройства
ТЕКСТ ПРОГРАММЫ
1 :
2 # @(#) whox v1.0 Who with expanded options Author: Russ Sage
2а Команда who с дополнительными опциями
4 XTRA="no"
5 SORT="sort -b +2"
6 DISPLAY="norm"
8 CUT1="cut -d' ' -f1"
9 CUT5="cut -d: -f5"
10 CUT6="cut -d: -f6"
12 for ARG in $@
13 do
14 case $ARG in
15 -f) DISPLAY="finger"
16 COMMAND="finger \$NAME; echo";;
17 -n) SORT="sort";;
18 -m) DISPLAY="mail";;
19 -p) DISPLAY="pass"
20 COMMAND="grep \"^\$NAME:\" /etc/passwd";;
21 -t) SORT="sort -b +2";;
22 -w) DISPLAY="write";;
23 -x) XTRA="yes";;
24 *) echo "whox: invalid option $ARG"
25 echo "usage: whox [-f] [-n] [-m] [-p] [-t] [-w] [-x]"
26 echo " -f finger users"
27 echo " -n sort by name"
28 echo " -m mail to each user"
29 echo " -p password info on users"
30 echo " -t sort by time (default)"
31 echo " -w show writeability of devices"
32 echo " -x extra home dir and gcos info"
33 exit 1;;
34 esac
35 done
37 if [ "$XTRA" = "yes" ]
38 then EXTRA="| while read LINE; do \
39 NAME=\`echo \$LINE | cut -d' ' -f1\`;\
40 ENTRY=\`grep \"^\$NAME:\" /etc/passwd\`;\
41 echo \"\$LINE\t\`echo \$ENTRY|\$CUT6\`\t\`echo \$ENTRY|\$CUT5\`
\";done"
42 else EXTRA=""
43 fi
45 case $DISPLAY in
46 norm) eval "who | $SORT $EXTRA";;
47 finger|pass) for NAME in `who | $SORT | cut -d' ' -f1`
48 do
49 eval $COMMAND
50 done;;
51 mail) who | cut -d' ' -f1 | while read NAME
52 do
53 echo "mail to $NAME (y/n): \c"
54 KB=`line " /dev/tty`
55 if [ "$KB" = "y" ]
56 then mail $NAME " /dev/tty
57 fi
58 done;;
59 write) ls -il `who | sed "s/...........\(.......\).*
/\/dev\/\1/"`;;
60 esac
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ
ARG Аргументы командной строки
COMMAND Команда, которую следует выполнить при использовании
команды who со списком имен
CUT1 Содержит синтаксис для выделения первого поля строки
CUT5 Содержит синтаксис для выделения пятого поля строки
CUT6 Содержит синтаксис для выделения шестого поля строки
DISPLAY Определяет, какой режим отображения использовать
ENTRY Запись в файле паролей для указанного пользователя
EXTRA Данная переменная содержит полный цикл shell-команд,
хранимых в виде одной строки
KB Входные данные от клавиатуры, полученные в цикле
NAME Содержит в каждый данный момент времени одно имя из
списка всех регистрационных имен
SORT Содержит выполняемый тип сортировки
XTRA Флаг, определяющий, должны ли быть активизированы
дополнительные опции
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ whox?
Как уже ранее обсуждалось в других местах этой книги, система
UNIX стремится обеспечить минимум возможностей в любой заданной области
интересов. Это не значит, что UNIX плохая система. Наоборот, она
делает гораздо больше, чем большинство других операционных систем. Но
очень часто мы хотим сделать немного больше того, что нам предоставляет
базовая система.
Получение информации о том, кто использует машину, может быть
применено для многих целей. Основная информация предоставляется командой
who, но она может быть не в том виде, который вам нужен для конкретной
цели. В любом случае, все, что делает команда who - дает вам
моментальную информацию о том, кто зарегистрировался. Нам необходим
некоторый способ автоматического доступа к этому списку зарегистрированных
имен и использования его для наблюдения, общения или других целей.
ЧТО ДЕЛАЕТ whox?
Whox - это инструментальное средство, расширяющее возможности команды
who. Оно не только может переупорядочить в соответствии с вашими
требованиями список, полученный от команды who, но также может указать
каждого зарегистрированного пользователя, передать почтовое сообщение
любому зарегистрированному пользователю, просмотреть парольную информацию
всех зарегистрированных пользователей и показать информацию,
взятую из индексного дескриптора файла, соответствующего терминальному
устройству каждого зарегистрированного пользователя.
По умолчанию действие whox заключается в печати обычного выхода
команды who в порядке времени регистрации от более раннего до более
позднего. Опция -x добавляет к этому списку информацию из регистрационного
каталога и поле комментария из файла паролей. Если эта опция -x
кажется вам знакомой, то так оно и есть, поскольку это то же самое,
что и команда info, представленная ранее.
Whox имеет четыре различных режима отображения. Первый это формат
обычного выхода команды who. Whox позволяет вам сортировать его двумя
разными способами. Опция -n сортирует по именам, а опция -t (которую
не нужно указывать, поскольку она используется по умолчанию) сортирует
по времени регистрации.
Второй режим отображения состоит из режимов указания и паролей,
включаемых опциями -f и -p. Основное отличие от первого режима заключается
в том, что выход команды who не печатается, а используется для
генерации списка имен пользователей, который применяется для других
целей. Мы указываем каждого пользователя или печатаем парольную запись
каждого пользователя. Выполняемая команда хранится в переменной, поэтому
мы можем иметь общий цикл, использующий особым образом переменные.
(Команда finger имеется в системе Berkeley UNIX и в некоторых
других, но не во всех реализациях. Посмотрите руководство, чтобы выяснить,
что выводится на экран по этой команде.)
Третий режим - это почтовый режим, в котором вы имеете возможность
посылки почтового сообщения каждому зарегистрированному пользователю.
Вам задается вопрос о том, действительно ли вы хотите сделать
это. Больше от вас ничего не требуется. Этот режим выбирает опция -m.
Последний режим - это режим записи на терминал. Режим записи (опция
-w) показывает информацию о файле терминала для каждого зарегистрированного
терминального устройства. Эта информация полезна, если вы
хотите использовать команду UNIX'а write. Посмотрев на права доступа к
файлу устройства пользователя, вы можете сказать, имеется ли у вас
возможность записать текст на его экран. Некоторые пользователи, которые
не хотят, чтобы их прерывали, закрывают право записи на их терминал,
выполняя команду "mesg n". Вопрос о праве записи касается любого
способа посылки текста в другой файл, а не только с использованием команды
write. Право записи также защищает от таких вещей, как "echo
hello " /dev/tty00".
Способ обработки аргументов в командной строке приводит к
несколько странному обращению с данной утилитой. Каждый аргумент проверяется
по порядку и устанавливает внутренние флаги. Если вы поставите
в конце списка какую-либо опцию, меняющую флаг, установленный одной
из предыдущих опций, то вы получите действие последней опции. (Другими
словами, некоторые опции взаимно исключают друг друга. Лучший способ
изучить это - внимательно прочитать исходный текст командного файла и
выполнить несколько экспериментов с различными наборами опций.)
Например, мы хотим указать каждого пользователя. Мы используем
опцию -f. Опция -f устанавливает в качестве режима отображения режим
указания. Если мы поместим опцию -w справа от -f, как в команде "whox
-f -w", то установится режим записи на терминал. Команда whox будет
считать, что вы вообще не указывали опцию -f. На самом деле это не
представляет большую проблему, если вы знаете, что делает каждая опция.
Случайно смешивая их в одной команде, вы можете получить несколько
странные выходные данные.
ПРИМЕРЫ
1. $ sh -x whox -x
Запуск интерпретатора shell в отладочном режиме выполнения, подача
ему командного файла whox в качестве данных, передача опции -x для
whox. Отладочный режим показывает присвоение значений переменным и вызовы
команд. (Мы видели это ранее.)
2. $ whox -n -x
Печать выходных данных команды who, отсортированных по именам и
выдача дополнительных данных.
ПОЯСНЕНИЯ
В строках 4-10 выполняется инициализация переменных. Переменная
XTRA устанавливается в значение "no". Эта переменная используется для
построения командной строки при использовании опции -x. Установка XTRA
в значение "no" означает, что действие по умолчанию - не получать дополнительную
информацию.
Умолчанием для сортировки является сортировка по времени регистрации.
Это указывается сортировкой по колонке, стоящей после второй
(+2) и игнорированием ведущих пробелов (-b) в строке 5. Такой же синтаксис
применен ниже с опцией -t. Опция -t здесь лишняя, но она делает
более понятной командную строку. Опция -n также изменяет синтаксис
сортировки, чтобы просматривать первую колонку, которая является
списком имен.
Строка 6 инициализирует обычный режим отображения, которым является
печать стандартного выхода команды who. Если присутствуют другие
опции, соответственно изменяется переменная DISPLAY.
Строки 8,9 и 10 инициализируют некоторые переменные для команд
вырезки, что делает более компактными последующие команды. Если некоторые
командные строки оказываются слишком длинными, вы можете поместить
нужный текст в переменные и подставить их при необходимости.
Переменная CUT1 выглядит так, как будто она должна работать, но она не
работает в моей системе. Почему она не работает, объясняется ниже.
Просто запомните, что эта строка никогда не используется в данной
программе. Мы пока оставляем ее, чтобы поговорить о ней позже. Вы можете
убрать ее, если хотите.
Строки 12-35 обрабатывают аргументы командной строки. Цикл for
подставляет в переменную ARG каждый параметр по порядку и выполняет
оператор case по значению ARG.
Если опцией является -f, строка 15 изменяет переменную DISPLAY в
режим указания. Она также подставляет в переменную COMMAND команду
finger, которая выполнится в следующем цикле. Причина, по которой нам
нужно иметь переменную, содержащую команду, заключается в том, что
данный цикл является общим циклом, применяемым для двух разных целей.
Для того чтобы один и тот же цикл справился с двумя различными задачами,
мы помещаем эти задачи в переменную и выполняем эту переменную.
Это значительно сокращает общее количество текста, хотя привносит некоторую
дополнительную работу. Обратите внимание в строке 16, что символ
$ экранирован в операторе присваивания. Это необходимо, ведь мы
хотим, чтобы переменная COMMAND содержала символьную строку $NAME, а
не значение, которое имеет переменная NAME после присваивания. Значение
NAME расшифровывается в цикле во время его выполнения.
Строка 17 обрабатывает опцию -n. Все, что здесь требуется - изменить
способ сортировки выхода команды who, чтобы отразить порядок по
именам. Поскольку имя находится в первой колонке выхода команды who, а
команда sort сортирует по умолчанию по первой колонке, то используется
команда sort без опций.
Строка 18 обрабатывает опцию -m для передачи почтовых сообщений.
Здесь мы должны изменить режим отображения на почтовый. Все, что нужно
для этого, находится в цикле mail, и не требуется инициализировать никакие
переменные.
Строки 19 и 20 справляются с опцией -p. Опция паролей изменяет
режим отображения на парольный режим и устанавливает команду, которую
мы вызываем, находясь в общем цикле. В данном случае мы используем команду
grep для получения парольной записи из файла /etc/passwd. Обратите
внимание, что в строке 20 мы используем внутренние двойные кавычки.
Для этого мы вынуждены экранировать их символами обратной косой
черты. Напомним, что обратная косая черта используется для отмены специального
значения особых shell-символов.
Строка 21 управляет опцией -t. Как уже упоминалось ранее, опция
-t в действительности не требуется в данной программе. Поскольку она
является умолчанием, требуемые для нее действия уже были предприняты в
начале программы - была выполнена точно такая же инициализация. Синтаксис
команды sort точно такой же, как и в строке 5.
Строка 22 обрабатывает опцию -w для показа возможности записи в
файлы терминалов. Единственное, что нужно здесь сделать - изменить режим
работы терминала.
Строка 23 управляет опцией -x. Поскольку для получения дополнительной
информации требуется довольно сложная инициализация, то мы
только устанавливаем в этом месте флаг XTRA, показывающий, что мы хотим
выполнить эту инициализацию позже.
Строка 24 - это улавливатель для обработки ошибок. Символ * соответствует
любому символу, который не был распознан ранее. Печатается
сообщение об ошибке и синтаксическая подсказка, и whox завершается.
Строки 37-43 устанавливают переменные, используемые в опции дополнительной
информации. Строка 37 проверяет, установлена ли переменная
XTRA в состояние "yes", что имеет место только тогда, когда в командной
строке имелась опция -x. Если это так, то в переменную EXTRA
заносится много всяких вещей, которые мы рассмотрим позже. В противном
случае в переменную EXTRA заносится пустая строка, так что она никак
не проявляется на стадии фактического выполнения.
Переменная EXTRA здесь очень важна и на самом деле делает небольшой
фокус. Мы собираемся поместить в переменную некоторый код, требуемый
для обработки опции -x. Поскольку дополнительная информация, которую
мы хотим получить, требует предварительно некоторой обработки, то
мы помещаем в переменную некоторый командный текст. Как только эта переменная
начинает выполняться, выполняется и этот командный текст. Это
похоже на макрокоманду, только ее текст находится фактически в исполняемой
программе.
Строки 38-41 вставлены внутрь переменной EXTRA. Это сделано путем
взятия в двойные кавычки всех четырех строк. Все специальные символы,
которые должны быть частью данных в этой переменной, должны быть экранированы
символами обратной косой черты. В строке 38 в переменную
EXTRA заносится символ конвейера (|) и начало цикла while. В конце
строки 38 имеется символ косой черты, указывающий интерпретатору
shell, что присваивание продолжается после символа конца строки (возврата
каретки или перевода строки).
Строка 39 присваивает переменной NAME значение поля, вырезанного
из данных, читаемых в цикле while. Напомним, что весь данный оператор
помещается внутрь переменной EXTRA. Когда я выше упоминал, что в строке
с переменной CUT1 есть проблемы, то как одно из таких проблемных
мест я имел в виду именно это. Когда я попытался использовать переменную
CUT1 в этом операторе вместо указания команды cut, shell не смог
правильно распознать этот оператор. Одинарные кавычки, отмечающие символ-разделитель
для вырезки, не были распознаны. В результате команда
cut считала, что символом-разделителем является символ ' и после этого
аварийно завершалась, поскольку второй символ ' представлял собой недопустимое
описание списка для опции -f. Строка с опцией -f шла позже,
но команда cut этого никогда не узнавала, поскольку аварийно завершалась
до этого. Когда я заменил переменную CUT1 просто командой cut,
эта проблема исчезла.
Давайте рассмотрим, как я отлаживал эту часть. Я использовал
shell с опцией -x, поэтому я мог следить за тем, что происходит. Как
вы можете видеть, когда переменная CUT1 была инициализирована, одинарные
кавычки находились все еще в операторе, но когда выполнялась
настоящая команда cut, одинарные кавычки уходили при синтаксическом
расширении. Для генерации такого списка данных я выполнил следующий
вызов: sh -x whox -x. Вот что я увидел:
XTRA=no
SORT=sort -b +2
DISPLAY=norm
CUT1=cut -d' ' -f1 "- Одинарные кавычки все еще здесь.
Основная проблема.
CUT5=cut -d: -f5
CUT6=cut -d: -f6
XTRA=yes
+ who
+ read LINE
+ sort -b +2
+ echo russ console Jun 20 14:11
+ cut -d -f1 "- Теперь выполняется правильно.
Кавычек нет.
Это сокращенная распечатка. Она показывает, что когда выполнялась
команда cut, она не имела одинарных кавычек. Когда же запускалась переменная
CUT1, она имела одинарные кавычки. Я не мог представить, как
избавиться от кавычек, поэтому я просто вставил вызов самой команды
cut обратно на это место. Может быть какой-нибудь молодой растущий
мастер сможет себе это представить.
Во всяком случае, вы можете видеть полезность отладки.
Цикл, выполняющий такое же присваивание, имеет такой вид при
обычном стиле записи на языке shell:
| while read LINE
do
NAME=`echo $LINE | cut -d' ' -f1`
ENTRY=`grep "^$NAME:" /etc/passwd`
echo "$LINE\t\`echo $ENTRY|$CUT6\`\t\`echo $ENTRY|$CUT5\`\"
done
Для того чтобы поместить такой же цикл в переменную, мы должны
экранировать в этом тексте все специальные символы.
Строки 45-60 - это оператор case, который реализует различные режимы
отображения. Строка 46 выполняет обычный режим отображения команды
who. Поскольку в обычном режиме имеется возможность использовать
переменную EXTRA, нам необходимо произвести повторный разбор командной
строки командой eval, чтобы эта переменная приняла свое истинное значение
во время исполнения. Обратите внимание, что в команде eval имеются
кавычки, заключающие всю командную строку. Это необходимо потому,
что вся строка является одним набором входных данных для команды eval.
Без кавычек команда eval не работала бы. Переменная EXTRA не подвергается
повторному разбору.
Строки 47-50 управляют режимами указания пользователя и выдачи
информации из файла паролей. Оба эти режима используют один и тот же
цикл. Цикл for использован для установки переменной NAME в значение
первого поля каждой строки, полученной от команды who. Для каждого
имени, вырезанного из результата работы команды who, выполняется повторный
синтаксический разбор командой eval переменной COMMAND (которая
была установлена в операторе case, выполнявшем разбор аргументов). Тем
самым повторно анализируются и выполняются команды, находящиеся в переменной
COMMAND. Для режима указания пользователя переменная COMMAND
содержит команду finger, а для режима паролей в COMMAND хранится команда
grep.
Строки 51-58 похожи на режим указания пользователя. Этот цикл тоже
требует имена от команды who, но вместо использования оператора for
мы используем метод прямой пересылки по конвейеру. Результат работы
команды who по конвейеру передается команде cut (переменная CUT1 и
здесь бы не работала), которая по конвейеру передает данные в цикл
чтения while. Обратите внимание, что в этом месте нет никакой сортировки.
По умолчанию результат команды who выводится в порядке номеров
терминальных устройств. Я не думаю, однако, что порядок вывода этих
данных имеет большое значение.
Для каждого имени пользователя выводится запрос о том, хотите ли
вы передать ему почтовое сообщение. При чтении ответа в строке 54
должна быть использована команда UNIX'а line. Почему? Потому что весь
цикл использует оператор read для чтения имен. Оператор read читает
только со стандартного ввода, который в данном случае привязан к конвейеру.
Для получения входных данных с клавиатуры мы должны использовать
команду line, которая получает их из файла /dev/tty. Это распространенный
способ чтения данных с клавиатуры из переадресованного
цикла.
Строка 55 проверяет, является ли ответом символ y. Если да, вызывается
команда UNIX'а mail, и снова ввод переадресовывается из файла
/dev/tty (поскольку строки почтового сообщения мы должны вводить с
клавиатуры.) В данном случае мы фактически переадресовываем стандартный
ввод для вызова подчиненного shell-процесса, выполняющего команду
mail. Без выполнения переадресации команда mail читает из файла
/dev/null, что нарушает выполнение всего цикла whox.
Строка 59 управляет режимом показа возможности записи на терминал.
Цель здесь такова - использовать одну команду ls и, применяя подчиненный
процесс, извлечь файлы терминальных устройств из выходных
данных команды who. Эти файлы являются вторым полем результата команды
who. Сначала запускается команда who, которая по конвейеру передает
свои данные команде sed.
Затем sed использует команду подстановки для отбрасывания всего,
кроме того, что ограничено символами \( и \). Последующая часть команды
подстановки ссылается на этот ограниченный участок с помощью обозначения
\1. Используя символ . как соответствующий любому символу
распечатки, мы должны всего лишь посчитать столбцы, которые нам нужно
вырезать. Кроме того, имена устройств в команде who не имеют префикса
/dev/, который нам необходим. Команда sed вставляет его перед текстом,
вырезанным из команды who. В результате команде ls дается список полных
маршрутных имен ко всем файлам устройств зарегистрированных пользователей.
Затем это выводится на экран.
ЗАЩИТА ЛИЧНОЙ ИНФОРМАЦИИ
Другой стороной осведомленности пользователя является обеспечение
разумной степени безопасности. Ваша потребность в защите информации
зависит от рода выполняемой вами работы и от чувствительности информации,
которой вы управляете. Однако все хотят секретности и чувства безопасности,
которое появляется вместе с обоснованной уверенностью в
том, что они не могут стать жертвой нарушения защиты информации. Так
же, как вы можете изготавливать приспособления, помогающие сделать ваш
дом менее привлекательным для грабителей, так и каждый пользователь
может сделать приспособления, которые помогают поддерживать секретность
и безопасность его работы. Подобно полиции, системные администраторы
обычно имеют дело только с нарушением защиты уже после свершившегося
факта. Вы можете использовать средства, представленные нами в данном
разделе, чтобы помочь уберечь ваши файлы от несанкционированного
просмотра или разрушения.
Любая компьютерная система требует некоторого рода защиты. Уровни
защиты включают в себя физическую защиту (центрального процессора,
дисков и терминалов), защиту файлов, защиту процессов и всей работающей
системы. В многопользовательской среде еще более важно усиливать
защиту. Каждый пользователь имеет право засекречивать и защищать свою
среду и свои файлы. Ни один компьютер не имеет стопроцентной защиты.
Ваша среда лишь настолько защищена, насколько вы сделали ее таковой.
Защитные мероприятия могут достигать такой степени, что начинают мешать
свободному обмену идеями и затруднять использование гибкости
системы или исследование ее новых аспектов. Лично я считаю, что пользователи
должны иметь свободу делать все, что они хотят, пока это не
вредит системе или другим пользователям. Большинство информации, приведенной
в данной книге, подтверждает это убеждение.
Тип защиты, который обсуждается в данной главе, касается личного
аспекта защиты пользователя системы UNIX. Я хочу, чтобы место, где физически
находятся мои разработки, мой регистрационный каталог и любые
процессы, которые я запускаю в системе, были защищены. Для полного
комфорта мне нужно знать, что никто не влезет в мои вещи (посредством
обычных методов доступа или взлома) и не будет заглядывать мне через
плечо, чтобы наблюдать, чем я занимаюсь.
В данном разделе мы рассмотрим инструментальные средства acme,
inuse и lock. Acme - это препроцессор к команде UNIX'а acctcom.
Acctcom выполняет довольно неплохую работу по отображению учетной информации,
но некоторые опции требуется указывать все время. Acme устанавливает
их для нас. Напомним, что учетные записи хранятся в виде
структуры, а не в текстовом виде, и поэтому мы заставляем acctcom показывать
их для нас.
Следующее средство, inuse, позволяет вам установить ваш терминал
как "занятый", когда вы куда-нибудь уходите. Это эффективно блокирует
его и не позволяет никому использовать его. Для реализации такого блокирования
представлены программы как на языке Си, так и на языке интерпретатора
shell.
Последнее средство, lock, используется для блокирования и разблокирования
прав доступа к файлу и является на самом деле простым интерфейсом
с командой chmod.

ИМЯ: acme

acme Отображение учетной информации обо мне
НАЗНАЧЕНИЕ
Генерирует опции, необходимые для вывода на экран информации обо
мне, которая хранится в учетном файле.
ФОРМАТ ВЫЗОВА
acme [-l] [-u]
ПРИМЕР ВЫЗОВА
acme -u Выводит всю учетную информацию о пользователе с
именем $LOGNAME
ТЕКСТ ПРОГРАММЫ
1 :
2 # @(#) acme v1.0 Give accounting info on me Author: Russ Sage
2а Дать учетную информацию обо мне
4 if [ "$1" != "-l" -a "$1" != "-u" ]
5 then echo "usage: acme [-l] [-u]" "&2
6 echo " -l for ttyline" "&2
7 echo " -u for user name" "&2
8 exit 0
9 fi
11 OPT=""
12 for ARG in $*
13 do
14 case $ARG in
15 -l) OPT="$OPT -l `basename \`tty\``";;
16 -u) OPT="$OPT -u $LOGNAME";;
17 *) OPT="$OPT $ARG";;
18 esac
19 done
21 echo "acctcom $OPT"
22 acctcom $OPT
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ
ARG Каждое значение, указанное в командной строке
LOGNAME Переменная среды, содержащая мое регистрационное имя
OPT Объединенный список всех опций и их аргументов
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ acme?
Большинство больших систем UNIX запускают стандартное программное
обеспечение для сбора учетной информации об использовании системы. Результаты
учетных транзакций передаются обычно в файл /usr/adm/pacct.
Фактически сбор учетной информации выполняется ядром системы. Каждый
раз при завершении процесса программы сбора учетной информации в ядре
производят одну запись. Переключателем, который включает и выключает
эту операцию, является acct(2). Команды пользовательского уровня также
взаимодействуют с системным вызовом (accton(1M)) и печатают результаты
сбора учетной информации (acctcom(1)).
Теперь, когда мы знаем, где находятся учетные записи и как они
туда попадают, нам нужно напечатать эту информацию. Acctcom может печатать
таблицы с информацией, но вам необходимо знать, какой использовать
индекс. Просмотр может производится по номеру терминальной линии
(это полезно, если идентификатор процесса был изменен командой
setuid), по имени пользователя, по группе, по времени и т.д. Я наиболее
часто использую опции поиска информации по номеру линии терминала
и по имени пользователя. С их помощью вы можете получить список всех
основных данных, имеющих отношение к вам. Когда вы вызываете acctcom с
этими опциями, вам необходимо указать дополнительную информацию, такую
как имя вашего терминала и ваше пользовательское имя. Было бы хорошо,
если бы мы могли уменьшить количество нажатий на клавиши и объем вычислений,
требуемых для получения информации. Для этого и предназначен
acme.
ЧТО ДЕЛАЕТ acme?
Acme - это интерфейсный процессор для команды acctcom(1). Он служит
для выдачи информации, которая требуется согласно указанным опциям.
Вы должны только дать командному файлу acme опции в сокращенном
виде, а все остальное он сделает сам. Если acme вызывается без аргументов,
программа acctcom выведет по умолчанию все записи.
Команда acctcom имеет много опций. В действительности мы используем
одну или две, но зато используем их часто. Двумя опциями, которые
понимает acme, являются -l и -u. Когда указана опция -l, acme получает
имя вашего терминала и помещает его в командную строку. Если указана
опция -u, acme получает ваше пользовательское имя и тоже вставляет его
в командную строку. Время от времени используются другие опции для
бавления специфической информации или небольшого изменения выходного
формата. Для того чтобы была возможность использовать другие опции команды
acctcom, acme включает в командную строку, формируемую для
fcctcom, любые дополнительные корректные опции acctcom, переданные в
командной строке для acme. Таким образом, acme поддерживает базовые
возможности, а кроме того позволяет вам подгонять команду под ваш
вкус.
Перед тем, как начнется выполнение команды acctcom, на экран выводится
расширенный вид командной строки, так что вы можете видеть командную
строку, сгенерированную acme. Без этого может получиться путаница,
поскольку вы не будете знать, что собирается делать программа.
ПРИМЕРЫ
1. $ acme
Выдача ВСЕХ моих учетных данных. Это последовательный список всех
команд, которые были запущены начиная с момента загрузки по настоящее
время. Счастливого чтения!
2. $ acme -u -b
Печать в обратном порядке всех учетных записей с моим пользовательским
именем. Обратный порядок означает - от самой последней из
предыдущих команд до моей первой команды.
3. $ acme -l
Вывод всех учетных записей для терминальной линии, которую я сейчас
занимаю. Сюда могут быть включены фоновые процессы, оставшиеся в
системе от предыдущих пользователей моей терминальной линии или даже
процессы, имеющие другие идентификационные номера пользователей (из-за
программ типа setuid, которые я мог запустить), которые запущены с
этой же терминальной линии.
ПОЯСНЕНИЯ
В строках 4-9 выполняется проверка на наличие ошибок. Если первый
позиционный параметр не -l и не -u, то это ошибка. Выводится сообщение
об этом и программа завершается.
В строке 11 переменная OPT инициализируется пустой строкой. Эта
переменная содержит все дополнительные опции acctcom и их аргументы.
Строки 12-19 представляют собой цикл for, который повторяется по
всем позиционным параметрам. Каждый аргумент сверяется в операторе
case с допустимыми опциями. Если опцией является -l (строка 15), в переменную
OPT заносится то значение, которое она уже имеет, опция -l и
добавляется имя терминального устройства, полученное от команды UNIX'а
tty. Команда tty выводит и префикс /dev, который не нужен. Для того
чтобы избавиться от этого префикса, мы берем из этой символьной строки
только основное имя.
Если указана опция -u, в переменную OPT добавляется -u и наше регистрационное
имя. Если аргументом являются любые другие данные, то
они просто добавляются в переменную OPT. Поступая таким образом, мы
можем передать в командной строке acme другие опции команде acctcom.
Обратите внимание, что здесь не выполняется проверка на ошибки в аргументах
командной строки. Вы можете ввести неверное значение, которое
нарушит работу команды acctcom. Однако это та цена, которую мы вынуждены
платить за гибкость при передаче аргументов в командной строке,
иначе нам придется значительно увеличивать текст командного файла.
После того обработки всех опций строка 21 выводит на экран командную
строку, которая должна быть выполнена, так что мы знаем, что
мы задавали. В строке 22 выполняется сама команда acctcom. Выходной
результат соответствует описанию acctcom(1).

ИМЯ: inuse

inuse Запретить использование терминала
НАЗНАЧЕНИЕ
Блокирует ваш терминал путем перевода в состояние занятости. Если
кто-либо попытается вторгнуться, вы это заметите.
ФОРМАТ ВЫЗОВА
inuse
ПРИМЕРЫ ВЫЗОВА
inuse Перевод терминала в состояние занятости
mypasswd Вводится мой пароль, но не отображается на экран
ТЕКСТ ПРОГРАММЫ
1 :
2 # @(#) inuse v1.0 Disable terminal and alert if used Author: Russ Sage
2а Запретить использование терминала и сообщить о попытке использования
4 trap "echo you\'re BUSTED!!; stty echo; kill $$" 2 15
6 PATH=/bin:/usr/bin
7 SECRET="secret"
9 stty -echo
10 echo "Lock string: \c"
11 read BUF1
12 echo
14 while :
15 do
16 BUF2=`line " /dev/tty`
17 if [ "$BUF2" = "$BUF1" ]
18 then break
19 elif [ "$BUF2" = "$SECRET" ]
20 then break
21 fi
22 echo "^G\c"
23 done
24 stty echo
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ inuse?
Рабочий день всегда загружен: много людей, еще больше бумаг, всякие
поручения и так далее и так далее. Когда вы покидаете свое рабочее
место, что вы собираетесь делать со своим зарегистрированным терминалом?
Каждый раз входить в систему и выходить из нее слишком долго, но
вы не хотите оставлять вашу работу открытой для всех. Вам необходима
программа, которую вы можете запустить на время вашего отсутствия и
которая не позволит другим людям использовать то, что вы делаете.
Конечно, не достаточно иметь некий процесс, который выполняется в
фоновом режиме, создавая впечатление, что терминал используется. Мы
должны перехватывать прерывания в случае, если кто-нибудь введет символы
"прерывания" или "выхода" с вашей клавиатуры. Единственным способом
разблокирования терминала должен быть ввод пароля или заранее определенного
слова, которое в любом случае разблокирует его.
ЧТО ДЕЛАЕТ inuse?
Inuse переводит ваш терминал в режим вечной работы. Это означает,
что терминал не отвечает на ваши запросы или запросы кого-то другого.
Когда вы готовы разблокировать ваш терминал, введите секретный пароль
или пароль, который вы придумали.
Когда вы первый раз вызываете inuse, у вас запрашивается пароль.
Эхо-отображение на терминал отключено, поэтому пароль не выводится на
экран. Для гарантии того, что никто не пытается переадресовать командному
файлу inuse какой-либо файл данных, все операции чтения производятся
непосредственно из файла терминального устройства /dev/tty, а не
через файловый дескриптор стандартного ввода. Это позволяет защититься
от попыток других пользователей вторгнуться в работу терминала путем
посылки на стандартный ввод файла большого размера, содержащего разные
слова.
После чтения вашего пароля inuse попадает в бесконечный цикл, который
читает символы с клавиатуры и сравнивает полученные входные данные
с двумя паролями. Каждый раз, когда кто-то вводит что-то некорректное,
выдается звуковой сигнал. Когда же введен один из допустимых
паролей, программа останавливается и терминал разблокируется.
Мы предлагаем две реализации inuse: командный файл интерпретатора
shell, приведенный выше, и программу на языке Си. Они выполняют одну и
ту же работу довольно похожими способами. Они демонстрируют, насколько
похожи эти два подхода, а их небольшие отличия рассматриваются ниже.
Сначала обсуждается командный файл, а затем Си-программа.
ПОЯСНЕНИЯ
Строка 4 инициализирует оператор trap. При активизации обработчика
trap выполняются три команды.
Целью применения ловушки trap является реагирование на любую попытку
прервать работу командного файла и прорваться на ваш терминал.
Первая команда выдает предупреждение о том, что вы вторгаетесь. Вторая
команда переключает терминал обратно в режим эхо-отображения, так что
все, что будет впоследствии введено с клавиатуры, отобразится на экране.
и последняя команда заставляет программу совершить самоубийство.
Как мы увидим позже, это самоубийство является особым родом прекращения
работы программы. Обращение к "самому себе" в операторе kill выполняется
с использованием метасимволов $$, которые представляют собой
идентификационный номер выполняющегося shell-процесса. Обработчик ловушек
включается сигналами 2 и 15, которыми являются прерывание и
программное завершение соответственно. Отметим, что сигнал выхода из
программы (сигнал 3) здесь не используется. Причину этого мы объясним
позже.
Строка 6 устанавливает маршрут, по которому может обращаться
inuse. Тем самым inuse никогда не сможет быть "застигнут врасплох"
кем-нибудь, кто проник незаметно (в "троянском коне"). Строка 7 инициализирует
секретный пароль значением "secret", которое совсем не секретно.
Вы можете изменить пароль на любое слово перед установкой командного
файла в вашей системе. В правах доступа к файлу, в котором
хранится текст данной shell-программы, вы должны запретить возможность
чтения. В противном случае другие пользователи смогут увидеть секретное
слово.
Строка 9 отключает эхо-отображение, строка 10 печатает запрос пароля,
а строка 11 читает пароль, который вы вводите, и заносит его в
переменную BUF1.
Строки 14-23 представляют собой вечный цикл while, который можно
прервать только вводом правильного пароля. Строка 16 читает ввод с
клавиатуры. При нажатии возврата каретки строка 17 проверяет, соответствует
ли то, что введено с клавиатуры, паролю пользователя. Если
нет, переменная BUF2 сравнивается с секретным паролем. Если какой-то
из паролей совпадает, оператор break производит выход из цикла while,
тем самым прекращая выполнение программы. Если введенные данные не
соответствуют ни одному из паролей, то в строке 22 выдается звуковой
сигнал и снова начинает выполняться оператор чтения клавиатуры.
Если пароль введен правильно, в строке 24 включается эхо-отображение
на терминал и программа завершается. Если происходит прерывание,
активизируется оператор trap. Данная операция подробно рассматривается
ниже.
ПОДРОБНЕЕ О ЛОВУШКАХ
Нам нужно рассмотреть смысл клавиши выхода из программы. Она производит
прерывание, похожее на все другие прерывания, но кроме того
выводит дамп памяти для запущенного процесса. Мы оставляем клавишу выхода
нетронутой оператором trap, поскольку она становится нашей
последней надеждой на приостановление командного файла inuse. Когда
ваш терминал заблокирован, он эхо-отображает вводимые с клавиатуры
символы, но не реагирует на них. Тот, кто нажимает на клавиши, видит
это и пытается выйти из ситуации, нажимая на клавишу прерывания (обычно
это клавиша DEL). Когда он это делает, на экран выводится сообщение
"you're busted", эхо-отображение снова включается и программа сама себя
уничтожает (сигнал 15). Когда сигнал уничтожения принимается программой,
этот сигнал ловится, печатается сообщение и программа снова
сама себя уничтожает. Эта последовательность выполняется снова и снова,
как в вечном цикле. Каждый раз, когда ловушка уничтожается и снова
запускается, используется стек. Если все это будет выполняться достаточно
долго, то весь стек заполнится записями об активизации и переполнится,
аварийно завершая весь сеанс работы.
Если клавиша выхода будет нажата до активизации оператора trap,
то программа завершится чисто. Если же клавиша выхода будет нажата
после начала работы оператора trap, то произойдет выдача дампа памяти
процесса и программа завершится. Это не совсем честный прием, но программирование
на языке shell вынуждено быть именно таким, и это предупреждает
вас о том, что что= то не в порядке.
Текущие значения клавиш для сигналов прерывания и выхода отображаются
командой stty(1). Эти значения можно переустановить в любые по
вашему желанию. У меня текущие установки такие:
speed 9600 baud; intr = DEL; quit = ^|; erase = ^h; kill = ^u;
eof = ^d;
Набрав на клавиатуре "stty intr z", вы можете установить символ z
в качестве сигнала прерывания ваших процессов, поэтому такое изменение
клавиши прерывания и запуск бесконечного цикла представляет собой еще
один способ защиты вашего сеанса работы. Поскольку вам потом нужно будет
вернуть старое значение, вы должны запомнить то, что вы делали.
Такой настройкой сигналов вы можете делать с вашим терминалом почти
все, что хотите. Этот подход дает меньшую степень защиты, чем перехват
прерываний, но может обеспечить вас минимальной защитой, не приводя к
выдаче дампа памяти.
Теперь мы представляем версию на языке Си.
ТЕКСТ ПРОГРАММЫ inuse НА ЯЗЫКЕ СИ
1 char id[] = "@(#) inuse v1.0 Disable terminal Author: Russ Sage";
3 #include
4 #include
5 #include
7 #define SSIZ 7
8 #define BSIZ 512
9 #define BELL "\07"
10 #define LF "\n"
12 main()
13 {
14 register int fd, sig, n;
15 char secret[SSIZ];
16 char buf1[BSIZ], buf2[BSIZ];
17 struct sgttyb sav_tty, chg_tty;
19 secret[0] = 's';
20 secret[1] = 'e';
21 secret[2] = 'c';
22 secret[3] = 'r';
23 secret[4] = 'e';
24 secret[5] = 't';
25 secret[6] = '\n';
27 buf1[0] = buf2[0] = '\0';
28 if ((fd = open("/dev/tty",O_RDONLY)) == -1)
29 exit(1);
31 for (sig = 2; sig "= 15; sig++)
32 signal(sig, SIG_IGN);
34 if (gtty(0, &sav_tty))
35 exit(2);
36 chg_tty = sav_tty;
37 chg_tty.sg_flags &= ~ECHO;
38 if (stty(0, &chg_tty))
39 exit(3);
41 write(1,"Lock string: ",13);
42 read(fd, buf1, BSIZ);
43 write(1, LF, 1);
45 for (;;) {
46 n = read(fd, buf2, BSIZ);
47 buf2[n] = '\0';
49 if (strcmp(buf2, buf1) == 0)
50 break;
51 if (strcmp(buf2, secret) == 0)
52 break;
53 write(1, BELL, 1);
54 }
55 stty(0, &sav_tty);
56 close(fd);
57 }
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖНА ПРОГРАММА inuse (Си)?
Версия inuse на языке Си работает почти так же, как и
версия на языке shell. Основное отличие заключается в том, что
командные файлы на языке shell пользуются командами раздела
(1), в то время как программы на Си используют команды разделов
(2) и (3).
ЧТО ДЕЛАЕТ ПРОГРАММА inuse (Си)?
Теоретические основы функционирования такие же, как и в
shell-версии. Инициализируется секретный пароль (в данном случае применяется
такой синтаксис, чтобы команда strings(1) не смогла посмотреть
его в исполняемом модуле), перехватываются сигналы, читается пароль
пользователя и начинается бесконечный цикл, который читает символы
с клавиатуры. Как только на клавиатуре что-то набрано и нажата клавиша
возврата каретки, входные данные сравниваются с двумя известными
паролями. Если они соответствуют одному из паролей, программа переустанавливает
терминал и завершается. Если совпадения не произошло,
терминал выдает звуковой сигнал и снова читает клавиатуру.
Поскольку здесь ловушки не работают, попытка прервать запущенную
программу не срабатывает. Единственный способ прекратить ее выполнение
- использовать команду "kill -9". Сигнал 9 является единственным, который
нельзя перехватить. Если бы это можно было сделать, то не было
бы никакого способа прекратить выполнение процесса, кроме как вытащить
вилку из розетки.
ПОЯСНЕНИЯ
Строка 1 помещает документирующую информацию в символьный массив.
При наличии этого текста в объектном модуле команда what(1) может вынуть
его оттуда, чтобы мы могли посмотреть его для идентифицирования
нашей программы.
Строка 3 подключает файл fcntl.h. Этот файл содержит все определения
языка Си для открытия, закрытия, чтения и записи файлов. Строка 4
подключает файл signal.h. Мы используем этот файл для определения переменной
SIG_IGN, которая является отметкой игнорирования сигналов
(signal_ignore). Строка 5 подключает файл sgtty.h, который мы используем
для определения всего, что относится к получению информации о
терминале посредством вызова ioctl(2).
Строка 7 определяет размер секретного пароля. Этот размер не обязательно
должен быть точно таким, как длина пароля. Этот размер указан
для удобства программирования.
Строка 8 объявляет размер буфера, в который каждый раз производится
чтение с клавиатуры. Хотя 512 символов слишком много для такого
считывания, на самом деле чтение прекращается с приходом символа возврата
каретки. Наличие большого буфера дает нам запас памяти.
Строки 9 и 10 определяют управляющие символы звукового сигнала и
перевода строки.
Строка 14 объявляет некоторые рабочие переменные. Обратите внимание,
что мы используем регистровые целые. Использование регистровых
переменных для ускорения работы - полезный прием. Если вы объявили
слишком много переменных по сравнению с количеством регистров в вашей
машине, не будет никакой ошибки. Оставшиеся переменные рассматриваются
как обычные переменные. Переменная fd используется в качестве файлового
дескриптора при открытии файла /dev/tty, переменной sig последовательно
присваиваются значения всех сигналов, а переменная n представляет
собой число прочитанных символов.
Строка 15 определяет секретный массив. Этот символьный массив содержит
наш секретный пароль, который прямо закодирован в программе.
Строка 16 определяет два буфера, в которые мы читаем вводимые символы.
Buf1 предназначен для нашего пользовательского пароля, а buf2 для попытки
ввода пароля, который считывается, когда мы хотим прекратить выполнение
программы. Строка 17 определяет две рабочие структуры, которые
содержат информацию об установках терминала (ioctl). Здесь у нас
две структуры, поскольку одна из них - первоначальная, а вторая - та,
на которую мы хотим изменить, чтобы не забыть первоначальные установки.
Строки 19-25 загружают пароль в секретный массив. Мы выполняем
посимвольное присвоение, поскольку при таком присвоении любая строка
символов в объектном модуле получается разорванной. Это мера безопасности
для предотвращения возможности зрительного просмотра с целью
извлечения ценной информации.
В строке 27 эти два буфера инициализируются в нулевой размер.
Строки 28 и 29 открывают устройство /dev/tty. Если возвращаемый
дескриптор файла равен -1, это говорит об ошибке и программа завершается.
Строки 31 и 32 перехватывают все сигналы. Цикл for работает с
сигналами, имеющими номера от 2 до 15. Для каждого из этих значений
выполняется системный вызов signal с целью игнорирования сигналов с
такими значениями.
В строках 34-39 выполняется модификация терминальных характеристик
для отключения эхо-отображения символов. Строка 34 получает информацию
об установках терминала в структуру sav_tty. Системный вызов
gtty - это просто программный интерфейс с системным вызовом
ioctl(get_values). Если этот вызов неудачен, программа завершается.
Строка 36 заносит данные из структуры sav_tty в структуру
chg_tty. Затем строка 37 присваивает элементу sg_flags результат операции
отрицания над его же значением и символом ECHO, что означает
"отключить эхо-отображение". После этого строки 38 и 39 записывают измененные
значения обратно на терминальное устройство. Системный вызов
stty - это просто программный интерфейс с системным вызовом
ioctl(set_values).
Строка 41 выводит на экран запрос на ввод пароля. Дескриптор файла
1 является стандартным устройством вывода, а 13 - длина строки символов.
Строка 42 читает BSIZ символов из файла /dev/tty. После чтения
на экран выдается символ перевода строки. Это необходимо сделать,
поскольку при отсутствии эхо-отображения на экран не выводится символ
перевода строки, когда вы вводите свой пароль. Поэтому мы вынуждены
вставить этот символ здесь сами.
Строки 45-54 представляют собой бесконечный цикл, который читает
символы с клавиатуры. Строка 46 выполняет чтение терминала для распознавания
пароля. В этой строке введенный пароль помещается в buf2, а не
в buf1. Мы выясняем количество символов, прочитанных в buf2 (n).
Поскольку индексирование массивов начинается с нуля, а не с 1, при
вводе n символов мы попадаем в конец текста и здесь мы вставляем ноль
для того, чтобы все, что было введено, представляло собой строку символов.
Мы делаем это потому, что команда read не производит обработку
символьной строки. Это делают системные вызовы stdio. Их описание находится
в разделе (3) руководства по системе, а не в разделе (2). Нам
нужно оформить прочитанные символы в виде строки, чтобы ее можно было
сравнить с паролями.
Строка 49 сравнивает то, что ввели с клавиатуры, с тем паролем,
который вы ввели в начале работы программы. Если эти символьные строки
одинаковы, strcmp возвращает значение ноль, которое сообщает о совпадении.
Команда break выводит выполнение из цикла for, и программа продолжается.
Строка 51 выполняет такое же сравнение с секретным паролем.
Если происходит совпадение, вы также выходите из цикла.
Если совпадения не произошло, строка 53 выдает на терминал звуковой
сигнал и управление передается оператору read в начало цикла for.
Если произошел выход из цикла for, управление передается строке
55. Происходит запись первоначальной информации об установках терминала,
тем самым включается эхо-отображение. Строка 56 закрывает файл
/dev/tty, и происходит нормальное завершение работы программы.
Вы могли видите, программа на языке Си, хотя и несколько сложнее,
чем на языке shell, но имеет некоторые преимущества. Она не может быть
остановлена или нарушена ничем, кроме команды kill -9. Пароль в исполняемом
модуле спрятан от любопытных глаз. Вы получаете больший уровень
защиты и более ясный подход за счет использования языка низкого уровня
и написания более длинной программы.

ИМЯ: lock

lock Блокирование и разблокирование файлов
НАЗНАЧЕНИЕ
Изменяет права доступа к файлам на запись и чтение, что выглядит
как блокирование и разблокирование.
ФОРМАТ ВЫЗОВА
lock [-u] file [...]
ПРИМЕР ВЫЗОВА
lock $HOME
Отключить возможность записи для меня и возможность чтения/записи
для группы и других пользователей по отношению к моему регистрационному
каталогу.
ТЕКСТ ПРОГРАММЫ
1 :
2 # @(#) lock v1.0 Lock and unlock files Author: Russ Sage
2а Блокирование и разблокирование файлов
4 if [ $# -eq 0 ]
5 then echo "lock: incorrect argument count" "&2
6 echo "usage: lock [-u] file [...]" "&2
7 exit 1
8 fi
10 if [ "`echo $1 | cut -c1`" = "-" -a "$1" != "-u" ]
11 then echo "lock: invalid argument $1" "&2
12 echo "usage: lock [-u] file [...]" "&2
13 exit 1
14 fi
16 MODE1="go-rw"
17 MODE2="u-w"
19 if [ "$1" = "-u" ]
20 then shift
21 MODE1="go+r"
22 MODE2="u+w"
23 fi
25 chmod $MODE1 $@
26 chmod $MODE2 $@
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ
MODE1 Режимы доступа к файлу, относящиеся к группе
пользователей и другим пользователям
MODE2 Режимы доступа к файлу, относящиеся к владельцу
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ lock?
Все файлы в системе UNIX имеют некоторые права доступа. Эти режимы
изменяются в соответствии с тем, как используется этот файл. Для
каждого файла должны быть установлены три набора прав доступа - для
владельца, группы и других пользователей. Обычно текстовые файлы имеют
режим 644, а исполняемые и каталоги - 755. Некоторые системы присваивают
по умолчанию другие значения.
Если вы хотите ограничить права чтения или записи, следует
использовать команду chmod(1). Новый режим должен указываться либо как
абсолютное восьмеричное число (например, 777), либо как буквенное выражение,
указывающее, какая категория пользователей что может делать
(например, ugo+rwx). Если вы хотите добавить или запретить определенные
возможности, легче использовать для этого буквенное выражение. Но
даже в таком случае нам будет полезно средство, позволяющее уменьшить
число нажатий на клавиши и избавляющее от необходимости точно запоминать
все, что касается прав доступа к файлу.
ЧТО ДЕЛАЕТ lock?
Lock - это средство, которое управляет правами доступа, обеспечивающими
безопасность всех ваших файлов. Этот командный файл обеспечивает
необходимые режимы доступа пользователей или запрета доступа в
ограниченной степени. Имея заранее определенные режимы доступа, наши
файлы лучше сохраняются в безопасном состоянии.
Входными данными для lock являются имена файлов. Допускается
использование символьной строки с любым набором имен файлов. В ней
должно находиться по крайней мере одно имя файла. Имена файлов с указанием
каталогов также допускаются.
Действие lock по умолчанию - блокирование указанного файла. Опция
-u разблокирует указанный файл.
Если команде chmod передано неверное имя файла, это создает проблемы
для нее и в этом случае выводится сообщение об ошибке.
ПРИМЕРЫ
1. $ lock -u $HOME/src *.c
Разблокирование моего каталога с исходными текстами и всех исходных
файлов на языке Си в текущем каталоге. Разблокирование дает возможность
чтения всем и возможность записи только мне.
2. $ lock $HOME/bin
Блокирует мой каталог bin так, чтобы никто не мог читать или
писать в него файлы. Даже хотя мой каталог нельзя читать, любой посторонний
может все же войти в него командой cd, если установлен бит x.
Если он попытается выполнить команду ls, каждый файл будет выдавать
сообщение об ошибке вида "filename not found" (файл с именем
"filename" не найден). Никто не может получить информацию из индексного
дескриптора файла, такую как временные характеристики и права
доступа, но любой может увидеть имена всех файлов из сообщения об
ошибке.
ПОЯСНЕНИЯ
Строки 4-8 проверяют счетчик аргументов. Если не был указан ни
один аргумент, выводится сообщение об ошибке. Должно быть указано хотя
бы одно имя файла.
Строки 10-14 проверяют, является ли первый символ первого позиционного
параметра знаком "минус" и отличается ли первая опция от допустимой
опции -u. Если эти условия выполняются, выводится сообщение
об ошибке и программа завершается.
Строки 16 и 17 инициализируют установки режимов прав доступа по
умолчанию. MODE1 устанавливается для запрета чтения и записи категориям
пользователей "группа" и "другие". MODE2 устанавливается для запрета
пользователю (т.е. мне) права записи. Это страховка для меня от
случайной записи в файл. Нам нужны две такие переменные, поскольку эти
два режима довольно разные. Единственный способ сделать это - дважды
вызвать команду chmod с двумя различными установками.
Строки 19-23 проверяют, была ли указана в командной строке опция
-u. Если была, она убирается из командной строки командой shift и переменные
режима инициализируются для разблокирования файлов. Строка 21
разрешает возможность чтения группе пользователей и другим. Строка 22
разрешает мне возможность записи. Обратите внимание, что в командном
файле lock не происходит модификации битов x, s или t. Это сделано намеренно,
поскольку бит x должен быть установлен только в случае, если
файл может быть исполняемым. Для каталогов бит x должен быть установлен
только в случае, если вы хотите, чтобы другие пользователи могли
заходить в этот каталог. Мы также никогда не устанавливаем возможность
записи для группы пользователей и для других пользователей, но мы отключаем
ее при блокировании файлов. Это дополнительная мера предосторожности
на случай, если файл имеет установленными такие права доступа
к нему, которые мы по каким-то причинам не желаем оставлять.
В строках 25 и 26 выполняется команда chmod. Параметр $@ использован
как обозначение всех имен файлов, указанных в командной строке.
Такой способ позволяет вам вызывать lock с несколькими именами файлов.
Из последних шести глав вы должны приобрести множество идей о
том, как можно обезопасить среду, в которой вы работаете, как иметь
дело с другими пользователями и вообще более продуктивно использовать
ваш рабочий день. Мы готовы двигаться дальше в понимании системы UNIX
и исследовании внутренней работы файловой системы, устройств и коммуникаций.
* ГЛАВА 7. Устройства и файловые системы *
ТЕРМИНАЛЬНЫЕ УСТРОЙСТВА
РАБОТА С КЛАВИАТУРОЙ
ФАЙЛЫ termcap
ДИСКОВЫЕ УСТРОЙСТВА
ФАЙЛОВЫЕ СИСТЕМЫ
ЗАГРУЗОЧНЫЙ ДИСК
РАЗМЕРНЫЕ ПАРАМЕТРЫ
c Быстрая очистка экрана
mntf Монтирование гибкого диска в системном дереве
mntlook Поиск всех монтированных файловых систем
umntsys Размонтирование всех файловых систем, кроме
корневой
lrgf Создание файла максимального размера,
допустимого в вашей системе
УСТРОЙСТВА И ФАЙЛОВЫЕ СИСТЕМЫ
ВВЕДЕНИЕ
Ниже уровня известной нам области файловых систем находится мир
устройств и их драйверов. В данной главе мы исследуем некоторые методы,
необходимые для работы с терминалами, дисками и непосредственно
файловыми системами. Программное средство 'c' иллюстрирует доступ к
терминалу на примере операции быстрой очистки экрана. Следующие три
средства - mntf, mntlook и umntsys - имеют дело с монтированием и размонтированием
файловых систем. Наконец, средство lrgf позволит вам
проверить пределы емкости вашей файловой системы.
СИСТЕМА UNIX И АППАРАТУРА
Перед тем, как углубиться в сущность вопроса, давайте обсудим некоторые
элементарные факты, которые мы должны помнить при рассмотрении
всех составляющих системы UNIX. Сердцем аппаратуры является центральный
процессор (CPU), который исполняет инструкции, управляющие машиной,
и фактически осуществляет всю работу. Операционная система необходима
для руководства работой, выполняемой центральным процессором, и
для обеспечения интерфейса между ним и ресурсами, требуемыми для того,
чтобы сделать что-то полезное: оперативной памятью, внешней памятью и
другими периферийными устройствами, такими как терминалы и принтеры.
Операционная система, особенно такая высокоразвитая, как UNIX,
имеет множество утилит и характерных особенностей, но сейчас речь не
об этом. Сердцем операционной системы (в данном случае UNIX) является
ядро (kernel). Ядро управляет процессами и руководит выполняемой работой.
Оно также является своего рода мостом между аппаратурой и внешним
миром. В данной главе мы обратим внимание на основные взаимоотношения
между ядром, процессами и аппаратурой.
В конечном итоге система должна взаимодействовать с внешними устройствами.
Наличие базовых знаний об устройствах весьма важно для полного
понимания того, как UNIX общается с внешним миром.
При работе с машиной много времени тратится на передачу данных в
машину и из нее, а это значит, что необходимо иметь дело со множеством
различных типов устройств, каждое из которых имеет свой "характер" и
особенности.
К нашему счастью, UNIX был разработан так, чтобы облегчить управление
данными и устройствами настолько, насколько это возможно. К нашему
несчастью, имеется, по всей видимости, несократимый объем знаний,
которыми мы должны овладеть обязательно. На рис. 7-1 показана общая
структура операционной системы UNIX. Мы видим, что со стороны ядра обращение
ко всем внешним периферийным устройствам выполняется как к
файлам устройств. Каждый тип устройств имеет свой собственный драйвер
и специфическую архитектуру, но обращение к каждому устройству выполняется
одинаковыми методами. Мы увидим, как использовать различные
способы доступа к устройствам и определим, какие способы наиболее эффективны.
Рис. 7-1
Модель среды системы UNIX

+-----------+
| |
+------------+ | Магнитная |
| Принтер | | лента |
+------------+ +-----------+
/dev/lp0 lpn /dev/rmt0 .../dev/rmtn
\ | | /
\ | | /
\ | | /
\ \ | | | | / /
+
+------- /dev/fd0
fd:1,2 | +-------------+ |--- +-----------+
/dev/tty00----| | ПРОЦЕССОР | | |Гибкий диск|
+--------+ — | | ЯДРО | |--- +-----------+
| | / | +-------------+ |-------- fdn
| Экран | +
+
+--------+ / / | | | \ \
+--------+ / | \
|Клавиат.| ttynn /dev/hd01 hdnn
+--------+ +----------+
fd:0 | |
| Жесткий |
| диск |
| |
+----------+

UNIX обращается к периферийным устройствам через "специальные
файлы". Имеется два типа специальных файлов: блочные и символьные. Оба
типа имеют свое предназначение и особенности. Блочный (например,
/dev/hd0) использует буферизацию и позволяет получить доступ к большим
объемам данных на жестком диске. Символьный (например, /dev/tty00 или
/dev/rfd0) не использует значительную буферизацию, а выполняет обмен с
устройством по одному символу за обращение. Даже несмотря на особые
свойства таких файлов, для них поддерживается все тот же механизм защиты,
что и для всех других файлов в системе.
Первая область, которую мы рассмотрим - терминальные устройства и
работа с ними. Представленные программы включают в себя средство под
названием 'c' для быстрой очистки экрана, а также пример программы,
которая считывает значения нажатых клавиш и выполняет опрос нажатия
одной клавиши. Мы также рассмотрим пример файла описания терминала
(termcap), который обеспечивает доступные определения характеристик
терминала.
Затем мы рассмотрим дисковые устройства - жесткие и гибкие диски.
Мы увидим, что имеются различные способы просмотра разделов диска с
использованием файлов устройств.
В дополнение к работе с устройствами мы рассмотрим файловые
системы на жестком диске. Всем нам известно, что система UNIX существенно
ориентирована на диски, поэтому чем больше мы знаем о файловых
системах, тем лучше для нас. Для более полного понимания разделов
диска и файловых систем мы представим три программных средства.
Средство lrgf проверяет граничные значения параметров файловой системы
путем создания файла наибольшего возможного размера в вашей системе.
Средство mntf обеспечивает удобный способ монтирования и размонтирования
гибких дисков. Наконец, средство mntlook выполняет поиск немонтированных
файловых систем, которые представляют собой потенциальную
опасность.
ТЕРМИНАЛЬНЫЕ УСТРОЙСТВА
Драйверы терминальных устройств являются одними из самых сложных
драйверов устройств. Причина этого заключается в том, что существует
множество уровней программного обеспечения, которые поддерживают характеристики
интерактивных терминалов. При работе терминала по последовательной
линии связи необходима мощная поддержка для того, чтобы
облегчить его использование. Различные установки, которые может иметь
терминал, программируются командами stty(1) и ioctl(2). Команда
termio(7) также описывает различные аспекты протокола работы терминала.
ПРОТОКОЛ ОПЕРАЦИЙ ВВОДА/ВЫВОДА ТЕРМИНАЛА
Протокол работы терминала представляет собой согласованный набор
сигналов, позволяющих системе правильно интерпретировать вводимые с
клавиатуры строки. Протокол необходим по четырем причинам. Первой является
поддержка входной обработки специальных символов, таких как
символы удаления и прекращения работы программы. Во-вторых, нам необходимо
поддерживать обработку выводимой информации, например, вставку
символов задержки или изменение последовательности возврат каретки/перевод
строки. Третьей причиной является поддержка режимов необработанного
и "канонического" ввода. Эти два режима позволяют пользовательским
программам получать данные или по одному символу, или по одной
строке. Последняя причина введения терминального протокола - желание
сделать так, чтобы пользователь мог сам изменять параметры конфигурации
терминала.
Содержимое терминальной подсистемы показано на рис. 7-2. Рисунок
разбит на три части: слева - область пользователя, посредине - область
ядра и справа - область устройства. Этот рисунок показывает, как передаются
данные между терминалами и программами пользователя.
Рис. 7-2.
Управление протоколом терминала

Область Область ядра Область
пользователя устройства
Процесс
+-------+ : :
|Текст | : +--------+ +--------+ +-------+ : dzrint()
|-------| : ttread() |канонич.| canon()|необраб.| ttin()|приемн.| : +-----+
|Данные | : /---|очередь |"-------|очередь |"------|буфер |"--|dbuf |
| | : / +--------+ +--------+ / +-------+ : | |
| +----+| : / структура структура / структура : +-----+
| |ubuf||"---/ clist clist / ccblock :
| +----+| : / :
| +----+| : +--------- / ttxput() :
| |ubuf||---- | :
| +----+| : \ +-------+ "--+ +--------+ : +-----+
|-------| : \ |выходн.| |буфер | : |dbuf |
|Стек | : \---"|очередь|-------"|передачи|
"| |
| | : +-------+ ttout()+--------+ dzxint() : +-----+
+-------+ : ttwrite() структура структура :
clist ccblock /|\
|
------+
Граница драйвера
устройства

Когда какой-либо процесс читает символы с устройства, данные начинают
двигаться от буфера драйвера устройства, который называется
dbuf. Из этого буфера данные попадают в приемный буфер, управляемый
ядром. Приемный буфер читается подпрограммой ядра с именем ttin(), и
данные помещаются в структуру clist, называемую необработанной очередью.
(Слово "необработанная" означает, что над символами пока что не
производилось никакой обработки.) В то же время символы также помещаются
в выходную очередь, что позволяет системе выполнять эхо-отображение
символов по мере их ввода.
После этого подпрограмма canon() превращает необработанную очередь
в каноническую. ("Каноническая" означает применение правил, которые
в данный момент установлены системой для выполнения специальных
функций над строкой текста, например для обработки символа удаления.)
Такая обработка позволяет преобразовать данные перед тем, как их получит
процесс пользователя. Последней подпрограммой является ttread(),
которая читает символы из канонического буфера в буфер пользовательского
процесса в области данных процесса.
Когда символы записываются из процесса пользователя на терминал,
они проделывают почти такой же маршрут в обратном направлении: от области
процесса к области драйвера устройства. Основное отличие в подпрограмме
записи заключается в том, что эти данные проходят на один буфер
меньше. От процесса пользователя символы передаются в выходную
очередь ядра подпрограммой ttwrite(), а затем в буфер передачи с помощью
подпрограммы ttout(). Из буфера передачи они пересылаются непосредственно
в приемный буфер драйвера устройства с помощью подпрограммы
dzxint().
ОПРЕДЕЛЕНИЕ ВВОДИМЫХ СИМВОЛОВ
Бывает так, что мы хотим увидеть, какие символы вводятся с клавиатуры.
Мы могли бы для этого написать программу, но UNIX предоставляет
встроенную возможность для этой цели.
Это команда od - восьмеричный дамп (octal dump). Такое название
осталось еще с тех давних времен, когда восьмеричное исчисление широко
применялось при отладке. К счастью, результат работы команды od можно
получить в символьном, шестнадцатиричном или десятичном виде. Фокус
использования команды od для проверки входных и выходных значений заключается
в том, что od читает стандартное устройство ввода по умолчанию,
если не указан файл. Например, вызов
$ od -cx
test string
^d
^d
даст такой результат:

|
| 0000000 6574 7473 7320 7274 6e69 0a67
| t e s t s t r i n g \n
| 0000014
| $
|
Здесь вызов команды od делается без указания имени файла в командной
строке и с применением стандартного вывода в качестве выводного
устройства. Мы используем опцию -cx для того, чтобы байты интерпретировались
как символы ASCII, а соответствующие 16-битовые слова отображались
в шестнадцатиричном виде. По мере того, как вы набираете символы,
они отображаются на экране, а команда od сохраняет их в своем
буфере. В конце строки нажмите возврат каретки, затем CTRL-D. Ввод
CTRL-D завершает чтение символов командой od и выдает распечатку, в
которой сверху будут шестнадцатиричные значения, а снизу символы в коде
ASCII.
Обратите внимание, что два символа, выводимые для каждого шестнадцатиричного
слова, располагаются в обратном порядке по сравнению с
двумя байтами, образующими это слово. Например, слово 6574 интерпретируется
как два символа, t и e, где 65 - код ASCII для символа e, а 74
- ASCII-код для символа t. Для того чтобы выйти из команды od, введите
еще один CTRL-D для прекращения блочного чтения. Если вы хотите еще
проверять символы, продолжайте их вводить. Команда od работает
несколько загадочно. Если вы введете достаточное количество символов,
она выдаст на экран информацию по нажатию только лишь возврата каретки.
Но если вы ввели всего несколько символов, требуется нажатие как
возврата каретки, ТАК И CTRL-D для получения результата на экране.
Теперь мы можем сделать один фокус - изменить канонический способ
обработки при чтении символов командой od. Это позволит нам увидеть
эффект от различных установок протокола работы. Для этого проверьте
текущие установки вашего терминала. В версии System V используйте команду
"stty -a", а в версии Berkeley вам нужно применить команду "stty
everything". System V выдает гораздо больше параметров, чем Berkeley.
(Наиболее популярные версии UNIX'а разработаны и поддерживаются следующими
фирмами: System V - фирмой AT&T Bell Laboratories, которая в
настоящее время называется Unix System Laboratories; BSD (Berkeley
Software Distribution) - Калифорнийским университетом в Беркли; XENIX
- фирмой Microsoft.- Прим. перев.) Ниже приводится пример из XENIX:

|
| speed 9600 baud; line = 0;
| intr = DEL; quit = ^|; erase = ^h;
| kill = ^u; eof = ^d; eol = ^`
| parenb -parodd cs7 -cstobp hupcl cread -clocal
| -ignbrk brkint ignpar -parmrk -inpck istrip -inlcr -igncr icrnl -iuclc
| ixon ixany -ixoff
| isig icanon -xcase echo echoe echok -echonl -noflsh
| opost -olcuc onlcr -ocrnl -onocr -onlret -ofill -ofdel ff1
|
После того, как мы почешем голову, мы увидим, что текущий флаг
для канонической обработки установлен в состояние "icanon". То есть,
мы можем видеть, что он включен, поскольку нет префикса в виде знака
минус (хотя это не является строгим правилом).
Что происходит при канонической обработке? Символ возврата на шаг
назад (backspace) является одним из важных вопросов. Когда вы вводите
символ CTRL-H, он поступает в необработанную очередь как литеральный
символ CTRL-H. Когда программа canon() читает CTRL-H, она понимает это
так: "Изменить CTRL-H на символ возврата на шаг назад, записать пробел
на место символа, затем сделать еще шаг назад." При эхо -отображении
вы получаете удаление символа на экране. Когда каноническая обработка
отключена, вы посылаете CTRL-H как обычные символы. Вот пример того,
как это выглядит:

|
| $ stty -icanon Отключение канонической обработки
| $ od -cx
| test string^h^h^h^h^h^hcase
| ^d...
|
| 0000000 6574 7473 7320 7274 6e69 0867 0808 0808
| t e s t s t r i n g \b \b \b \b \b
| 0000020 6308 7361 0a65 0a04 0a0a 0a0a 0a0a 0a0a
| \b c a s e \n 004 \n \n \n \n \n \n \n \n \n
|
После слова "string" вы видите группу чисел 08, которые в
ASCII-коде обозначают CTRL-H. Эти числа 08 показывают вам, что литеральные
символы CTRL-H действительно присутствуют в их "необработанной"
форме. Поскольку CTRL-H не является больше специальным символом,
команда od рассматривает его подобно любому другому символу. Здесь
возникает новая проблема: поскольку специальные символы не распознаются,
мы потеряли возможность завершить блочное чтение вводом символа
конца файла (EOF). Когда вводится CTRL-D, он понимается просто как еще
один символ. Поэтому мы должны заполнить буфер команды od, чтобы
заставить ее выполнить дамп. В нашем примере CTRL-D - это символ 004
после символов case \n.
Кстати, в системе Berkeley используются установки "обработанная"
("cooked") и "необработанная" ("raw") для stty, которые по существу
служат для тех же целей, что "canon" и "-canon" в System V.
ДИНАМИЧЕСКОЕ ПЕРЕОПРЕДЕЛЕНИЕ СИМВОЛОВ ПРЕРЫВАНИЯ
Обратите внимание, что в предыдущей распечатке команды stty -a
присутствуют определения символов "intr" и "quit". Это две функции,
которые прерывают работу ваших работающих процессов. Строки intr и
quit представляют собой особые функции, а не просто нажатие клавиши.
Эти функции можно назначить на любую клавишу клавиатуры при помощи команды
stty.
Если мы изменили значение "intr" на другой символ, то этот новый
символ прервет ваши процессы. Вы даже можете установить в качестве
клавиши прерывания обычный символ. Вот как это можно сделать:
$ stty intr x
$ this is a junk stringx
$
Когда вы вводите символ x в конце строки, то вся введенная строка
обрывается и вы получаете новый символ приглашения. Для того чтобы
вернуться к обычному режиму работы, введите в качестве символа прерывания
старый символ. Если старым символом был "delete", вы даете такую
команду:
$ stty intr DEL
Что же в этом хорошего? Это показывает, насколько гибко работает
команда stty с терминалом, и может быть использовано в качестве личной
меры безопасности, чтобы сбить с толку человека, который захотел бы
произвести какой-либо беспорядок с вашего терминала. Когда вам нужно
на минуту отойти от терминала, измените ключ прерывания на какой-либо
другой и запустите командный файл вроде такого:
while :
do
:
done
Этот бесконечный цикл будет выполняться постоянно. Если кто-нибудь
попытается прервать этот процесс, используя клавишу DEL, ничего
не произойдет. Когда вы вернетесь, нажмите новую клавишу прерывания.
Она прервет цикл, и вы получите символ приглашения.
Очень важным является также символ "eof". Он соответствует концу
файла (end of file), и обычно им является CTRL-D. Это объясняет, почему
вы выходите из системы UNIX с помощью CTRL-D. Когда вы посылаете
CTRL-D вашему регистрационному shell, вы тем самым говорите ему:
"Shell, это метка конца файла для данного сеанса работы". Поскольку
терминал рассматривается как файл, CTRL-D закрывает файл, а shell, который
работает как цикл вида "читать команды с клавиатуры, пока не
встретится EOF", завершается, тем самым посылая сигнал программе init.
Программа init порождает команду getty по данной терминальной линии,
которая выводит на экран приглашение "login:" для ожидания команды от
следующего пользователя. Если вы измените символ конца файла, вы больше
не сможете выйти из shell по CTRL-D. Он будет выводиться на экран
точно так же, как любой другой символ. Вот пример таких действий:
$ stty eof x
$ x
login:
Теперь признаком конца файла является обычный символ x. Когда вы
вводите символ x, это равносильно вводу CTRL-D, и вы выходите из
системы. Очевидно, такие манипуляции нежелательны, однако это позволяет
показать ту большую степень гибкости, которую использует UNIX при
присвоении различных функций разным символам.
ФАЙЛЫ ТЕРМИНАЛЬНЫХ УСТРОЙСТВ
Физически адресация терминалов производится посредством файлов
устройств в каталоге /dev. Когда вы регистрируетесь в системе, вам
присваивается определенный номер терминала, например tty01. Этот номер
терминала в действительности является файлом /dev/tty01. Если вы вызовете
команду tty UNIX, она выведет полное маршрутное имя файла того
терминального устройства, за которым вы работаете.
Файлы терминальных устройств выглядят так же, как обычные файлы,
за исключением того, что команда "ls -l" показывает, как называются
старший и младший номера устройства, которые не являются частью обычных
файлов. Старший номер является индексом в таблице cdevsw[], которая
содержит адрес драйвера устройства, используемого ядром для данного
типа устройства. Младший номер идентифицирует конкретное физическое
устройство. Эти номера появляются в последовательном порядке для всех
устройств, использующих один и тот же драйвер. Так выглядит типичный
список файлов устройств в системе XENIX:

|
| crw--w--w- 1 russ tricks 0, 0 Jun 22 02:34 /dev/console
| crw--w--w- 1 russ tricks 0, 1 Jun 22 00:41 /dev/tty02
| crw--w--w- 1 root tricks 0, 2 Jun 21 17:56 /dev/tty03
| crw--w--w- 1 root tricks 0, 3 Jun 21 05:47 /dev/tty04
| crw-rw-rw- 1 root root 0, 4 Feb 18 17:09 /dev/tty05
| crw-rw-rw- 1 root root 0, 5 Feb 18 17:09 /dev/tty06
| crw-rw-rw- 2 root root 5, 0 Jun 21 20:23 /dev/tty11
| crw--w--w- 2 root tricks 5, 8 Jun 22 02:20 /dev/tty12
| crw-rw-rw- 2 root root 5,128 Feb 18 17:09 /dev/tty13
| crw-rw-rw- 2 root root 5,136 Feb 18 17:09 /dev/tty14
|
По символу 'c' в первом столбце мы видим, что это символьные устройства,
а биты прав доступа показывают, кто имеет доступ к этим файлам.
Первый столбец чисел (0 или 5) является старшим номером. Младшие
номера в следующем столбце обычно следуют в последовательном порядке,
но не всегда (как видно в данном примере).
В дополнение к использованию абсолютного номера вашего терминала,
одно из устройств используется в качестве "логического", или "родового"
адреса вашего терминала. Оно использует другой драйвер устройства,
называется /dev/tty и применяется в случаях, когда стандартный ввод и
стандартный вывод переадресовываются в другие файлы, а прикладной
программе необходимо читать с клавиатуры или писать на экран. При помощи
доступа к файлу /dev/tty образуется связь с самим терминалом. Выбор
использования устройства с именем tty вместо устройства tty01
главным образом зависит от того, какая стоит задача. Если вам необходимо
иметь независимую от типа терминала программу, используйте
/dev/tty.
ПРАВА ДОСТУПА К ТЕРМИНАЛУ
Поскольку терминальное устройство является файлом, оно имеет режимы
прав доступа точно так же, как и все другие файлы. Эти режимы
представляют собой защиту доступа к вашему терминалу. Если все пользователи
имеют право записи на ваш терминал (это обозначается как
rw--w--w-), то они могут записать на ваш экран все, что угодно, и вы
никогда не узнаете, кто это сделал. Если вы хотите предупредить это,
то выполните команду "chmod 600 `tty`", где символы ударения и обозначение
tty соответствуют маршрутному имени вашего терминального файла.
Более простой в использовании является команда UNIX'а mesg. Команда
"mesg n" запрещает запись извне на ваш терминал. Ваши собственные процессы
по-прежнему имеют доступ на запись.
Права доступа к терминалу связаны также с проблемой безопасности,
которую мы рассмотрим в главе 9. Пока что отметим, что всякий раз,
когда вы открываете файл (то ли для чтения, то ли для записи), вам
возвращается дескриптор файла. Затем вы можете использовать этот дескриптор
файла в системном вызове ioctl. Получение этого дескриптора
файла подобно получению ключа к терминальному интерфейсу определенного
пользователя. Любые изменения, производимые с помощью ioctl с данным
дескриптором файла, вызывают немедленный эффект, и нарушитель защиты
может читать все, что записывается или считывается с вашего терминала
или даже заставить ваш терминал выполнять команды, которые присваивают
себе неразрешенные права! Пользователь, который работает с этим терминалом,
может никогда не узнать, что же произошло или кто это сделал.
Другим примером подобного рода является команда write(1). Она
используется для установки связи по линии в реальном режиме времени,
или "болтовни". Она общается с терминалом путем выполнения записи в
файл устройства. Измените биты прав доступа, выключив их командой
"mesg n", и никто не сможет выполнить команду write с вашим терминалом.
Таким способом вы можете "снять трубку телефона", когда вы хотите,
чтобы вам не мешали. Вместе с тем кто-нибудь мог бы сделать такое:
$ while :
" do
" clear " /dev/tty00
" done &
При этом создается фоновый бесконечный процесс (пока он не будет прекращен
командой kill или выходом из системы), который посылает пользователю
терминала tty00 символы очистки экрана. Как только этот пользователь
что-нибудь набирает на экране, он тут же очищается. Большинство
пользователей не могут даже понять, что происходит. Если это случится
с вами, попытайтесь отключить права доступа к вашему терминалу. Если
же тот, кто это делает, является суперпользователем (root), то никакие
права доступа не смогут остановить его, поскольку суперпользователь не
имеет ограничений по правам доступа к файлам. В этом случае возникает
проблема для системного администратора!
Еще одна странная вещь наблюдается в случае, когда кто-либо посылает
что-то на ваш терминал командой write, а вы отключаете права
доступа. Команда write по-прежнему имеет доступ к вашему терминалу,
пока она не закроет устройство. После закрытия она больше не имеет
прав для открытия устройства. Представляется странным факт, что после
получения доступа к терминалу последующее запрещение права доступа не
оказывает никакого воздействия до тех пор, пока вы не прекратите работу
с терминалом.
ОТМЕТКИ О ВРЕМЕНИ ДОСТУПА К ТЕРМИНАЛУ
Еще одним атрибутом терминалов, который вызван тем, что терминалы
- это файлы, являются даты модификации, доступа и обновления. Каждый
файл в системе имеет эти три даты, которые содержатся в его индексном
дескрипторе (в секундах) в виде ДЛИННЫХ (long) чисел.
Эти даты могут содержать некоторую интересную информацию. Как было
отмечено при описании командного файла activ в предыдущей главе,
дата модификации представляет собой последний момент времени, когда
пользователь что-то набирал на своей клавиатуре. Другие даты тоже
кое-что означают, но они не так часто используются.
ОБРАБОТКА ТЕРМИНАЛОМ ВВОДИМОЙ ИНФОРМАЦИИ
Как уже обсуждалось ранее, по умолчанию драйвер терминала работает
в каноническом режиме, т.е. в режиме построчной обработки. Когда вы
вводите символы, драйвер ожидает, пока вы нажмете возврат каретки,
после чего передает для обработки всю строку. Если вы работаете не в
каноническом режиме, то каждый символ передается для обработки непосредственно
после ввода. Наглядным примером такого режима работы является
редактор vi. Вы нажимаете по одной клавише для движения курсора,
удаления символов и т.д., поэтому редактор vi, очевидно, должен
получать каждый символ сразу же, как только нажата клавиша.
Каким образом это делается в программе? Прием старый и часто
используется в UNIX, хотя и не очень хорошо описан в документации. Такого
рода информацию можно добыть путем просмотра большого количества
текстов программ. Необходимо отметить, что этот прием лучше всего работает
в программах на языке Си. Командные файлы, написанные на языке
shell, могут использовать для этой цели команду stty, но результат будет
не один и тот же. Следующий фрагмент программы на языке Си отключает
каноническую обработку, затем читает символы и выводит их на экран.
1 #include
3 struct termio tsav, tchg;
5 main (argc, argv)
6 {
7 int c;
9 if (ioctl (0, TCGETA, &tsav) == -1) {
10 perror("can't get original settings");
невозможно получить исходные установки
11 exit(1);
12 }
14 tchg = tsav;
16 tchg.c_lflag &= ~(ICANON | ECHO);
17 tchg.c_cc[VMIN] = 1;
18 tchg.c_cc[VTIME] = 0;
20 if (ioctl (0, TCSETA, &tchg) == -1) {
21 perror("can't initiate new settings");
невозможно задать новые установки
22 }
24 while (1)
25 {
26 c = getchar();
28 if (c == 'x')
29 break;
31 putchar©;
32 }
34 if (ioctl(0, TCSETA, &tsav) == -1) {
35 perror("can't reset original settings");
невозможно вернуть исходные установки
36 exit(3);
37 }
38 }
У нас есть две "терминальные" структуры данных, одна из которых
содержит исходные установки, а другая - установки, которые мы изменяем
и записываем. Первый системный вызов ioctl получает информацию об
установках терминала. Затем мы присваиваем эти значения изменяемой
структуре (строка 14). Модификации терминального интерфейса мы выполняем
в строках 16-18. Строка 16 отключает каноническую обработку и
эхо-отображение символов. Строка 17 устанавливает, что минимальное количество
нажатий на клавиши равно одному. Строка 18 определяет, что
время ожидания для повторного чтения данных равно 0. По существу, это
блочное чтение по одному символу.
Новые значения терминальных характеристик устанавливаются в строке
20. В этот момент режим работы терминала меняется. Цикл while читает,
проверяет и выводит символы. Только при вводе символа x цикл завершается,
терминал устанавливается в первоначальное состояние, и
программа заканчивает работу.
Как мы уже заметили, операция чтения здесь является блочной. Это
значит, что программа ожидает, пока вы введете символ. Если вы ничего
не вводите, программа находится в бесконечном цикле ожидания. Каким
образом мы изменяем режим чтения с блочного на посимвольный?
Этот вопрос эквивалентен такому вопросу: "Как опросить клавиатуру
в UNIX?". Опрос является весьма важным приемом для некоторых применений.
Опрос работает примерно так: "Посмотреть на клавиатуру. Если введен
символ, получить его и каким-то образом обработать. В противном
случае продолжать делать то, что вы делали. После истечения интервала
времени, определенного программой, проверить клавиатуру снова." Таким
образом, если пользователь не нажимает на клавиши, программа продолжает
работу, а не ожидает, пока что-нибудь будет нажато на клавиатуре.
Для выполнения такой работы нам нужно несколько более подробно
рассмотреть терминальный интерфейс. Как было отмечено ранее, терминал
представляет собой файл. Это значит, что он должен обладать всеми
обычными свойствами файлов - возможностью открытия, закрытия, чтения,
записи и т.д. Мы также видели, что терминалы имеют протокол работы,
который может быть изменен командой stty. Мы видели, что для получения
одного символа с клавиатуры используется протокол работы. Теперь мы
увидим, что для выполнения опроса вы должны использовать технику, которая
относится к файлам, а не ioctl.
Секрет здесь в том, чтобы открыть терминальный файл, изменяя режим
его открытия. Затем для получения одного символа используется тот
же текст, что и в предыдущем случае - тем самым опрос достигнут. Вот
текст программы:
1 #include
2 #include
4 struct termio tsav, tchg;
6 main (argc, argv)
7 {
8 int c;
10 /* change the terminal based on file primitives */
изменить режим терминала с помощью файловых примитивов
11 close(0);
12 if (open("/dev/tty",O_RDWR|O_NDELAY) == -1) {
13 perror("can't open tty");
невозможно открыть tty
14 exit(1);
15 }
17 /* change the terminal based on line disciplines */
изменить режим терминала с помощью протокола работы
18 if (ioctl (0, TCGETA, &tsav) == -1) {
19 perror("can't get original settings");
невозможно получить исходные установки
20 exit(2);
21 }
23 tchg = tsav;
25 tchg.c_lflag &= ~(ICANON | ECHO);
26 tchg.c_cc[VMIN] = 1;
27 tchg.c_cc[VTIME] = 0;
29 if (ioctl (0, TCSETA, &tchg) == -1) {
30 perror(can't initiate new settings");
невозможно задать новые установки
31 }
33 while (1)
34 {
35 putchar('.');
36 c = getchar();
38 if (c == 'x')
39 break;
41 putchar©;
42 }
44 if (ioctl(0, TCSETA, &tsav) == -1) {
45 perror("can't reset original settings");
невозможно вернуть исходные установки
46 exit(3);
47 }
48 }
Основное изменение производится в строках 11-15. Закрытие файла с
нулевым дескриптором (который обозначает стандартное устройство ввода)
закрывает стандартный ввод. Затем мы снова открываем файл /dev/tty.
Значение дескриптора файла равно нулю, так что мы переназначили стандартный
ввод на новое устройство. Фокус в том, что при открытии файла
используется режим, называемый NODELAY. Это означает, что когда выполняется
чтение по данному дескриптору файла (т.е. чтение стандартного
ввода), вместо ожидания ввода выполняется просмотр, есть ли там
что-нибудь, а затем работа продолжается.
В бесконечном цикле строка 35 печатает точку. Когда вы запускаете
эту программу, на экран выводится точка, как только программа попадает
в цикл. Если вы ждете, то продолжают выводиться точки. Как только вы
нажмете клавишу, выполнится эхо-отображение символа в промежутке между
выводом точек. Это демонстрирует, что программа работает в то время,
когда вы ничего не делаете.
ВОЗМОЖНОСТИ ТЕРМИНАЛОВ
Теперь, когда мы имеем понятие о характеристиках терминальных интерфейсов,
давайте перейдем к возможностям терминалов. ВОЗМОЖНОСТИ -
это те функции, которые выполняет аппаратура терминала. Если мы знаем
эту информацию, мы можем создать список возможных функций и использовать
его, например, для работы редактора vi. Это осуществляется при
помощи специального файла данных termcap (terminal capabilities - возможности
терминала), который описывает возможности терминала.
Большинство из существующих типов терминалов уже занесены в файл
termcap. Это файл /etc/termcap. Файл termcap и редактор vi происходят
из системы Berkeley. Такая комбинация оказалась настолько эффективной,
что была перенесена в System V. В более поздней System V Release 3
файл termcap уже не используется, его заменяет файл terminfo фирмы
AT&T. Мы применяли файл terminfo совместно с командным файлом today в
главе 5, но подробное обсуждение terminfo выходит за пределы нашей
книги. В системе Berkeley файл termcap по-прежнему остается стандартом,
и он заслуживает более детального рассмотрения.
Имеется документация по termcap, но не думайте, что вы из нее
много узнаете. В документации приводятся имена и однострочные описания
поддерживаемых функций, но нет информации о том, как формировать из
ничего записи этого файла. Самое лучшее, что мы можем посоветовать,
это взять имеющуюся запись и изменить ее.
В качестве примера мы приводим запись файла termcap для компьютера
Apple II. Это описание распространено в различных формах, но наш
пример относится к видеоплате Videx UltraTerm для Apple II+. Заметим,
что возможности, предоставляемые файлом termcap, являются обычно подмножеством
тех возможностей, которые фактически предоставляет аппаратура.
В частности, видеоплата в компьютере Apple выполняет некоторые
функции, которые не умеет делать файл termcap, например комбинации
настроечных битов для изменения видеоатрибутов. Самое большее, что мы
можем сделать с видеоатрибутами посредством файла termcap, это включить
или выключить инверсное отображение.
С другой стороны, некоторые типы аппаратуры не обладают всеми
возможностями, обеспечиваемыми файлом termcap. Например, одной из
функций, которой недостает в Apple, является функция прокрутки
("scroll reverse"). Аппаратура не делает этого, поэтому и в termcap
нет необходимости иметь описание этой функции. Вместо скроллинга
(прокрутки) вниз, отображаемый на экране текст продолжает выводиться в
верхней строке.
Для того, чтобы получить представление о том, как termcap соотносит
общие характеристики терминала с конкретными возможностями,
сравним терминалы Apple и vt52. Две соответствующие записи в termcap
имеют много похожих функций, но совершенно разные коды для их выполнения.
Приведем пример содержимого файла termcap:
a2|aii|Apple II with UltraTerm :\
:bl=^G:\
:bs:\
:cd=^K:\
:ce=^]:\
:cl=^L:\
:cm=^^%r%+ %+ :\
:co#80:\
:cr=^M:\
:do=^J:\
:ho=^Y:\
:kb=^H:\
:kd=^J:\
:kl=^H:\
:kr=^\\:\
:ku=^_:\
:le=^H:\
:li#24:\
:nd=^\\:\
:nl=^J:\
:se=^O:\
:so=^N:\
:up=^_:
В табл. 7-1 представлен список функций файла termcap с сопоставлением
терминалов Apple и vt52. Если какая-либо функция отсутствует у
одного или другого терминала, это отмечается словом "нет".
Таблица 7-1
Терминальные возможности и их конкретные значения

Функция Apple II vt52

bl - звуковой сигнал ^G ^G
(bell)
bs - возврат на шаг по коду ^H да да
(can backspace with ^H)
cd - очистка до конца экрана ^K \EJ
(clear to end of display)
ce - очистка до конца строки ^] \EK
(clear to end of line)
cl - очистка всего экрана ^L \EH\EJ
(clear entire screen)
cm - движение курсора ^^%r%+ %+ \EY%+ %+
(cursor motion)
co - число позиций в строке #80 #80
(number of columns in a line)
cr - возврат каретки ^M ^M
(carriage return)
do - сдвиг на строку вниз ^J ^J
(down one line)
ho - курсор в начало экрана
(без команды cm) ^Y \EH
(home cursor)
kb - код клавиши backspace ^H ^H
(sent by backspace key)
kd - код клавиши "стрелка вниз" ^J \EB
(sent by down arrow key)
kl - код клавиши "стрелка влево" ^H \ED
(sent by left arrow key)
kr - код клавиши "стрелка вправо" ^\\ \EC
(sent by right arrow key)
ku - код клавиши "стрелка вверх" ^_ \EA
(sent by up arrow key)
le - курсор влево ^H ^H
(cursor left)
li - число строк экрана #24 #24
(number of lines per screen)
nd - нестирающий пробел ^\\ \EC
(nondestructive space)
nl - символ перевода строки ^J ^J
(newline character)
pt - наличие аппаратной табуляции нет да
(has hardware tabs)
se - обычный экран ^O нет
(end stand out mode (normal))
so - инверсный экран ^N нет
(begin stand out mode (inverse))
sr - прокрутка нет \EI
(scroll reverse)
ta - символ табуляции ^I ^I
(tab)
up - сдвиг вверх на строку нет ^_
(up a line)

Самое интересное здесь, наверное, то, что терминалы vt52 и Apple
имеют взаимно обратный порядок указания координат в команде движения
курсора. Терминал vt52 воспринимает значения x и y в порядке YX, что
является умолчанием для файла termcap. Apple воспринимает их в порядке
XY, поэтому в записи файла termcap требуется поменять координаты
местами, что указано обозначением %r в функции cm.
Файл termcap позволяет вам спрятать основную информацию о специфических
характеристиках терминала (за исключением характеристик, которые
могут отсутствовать у терминала, или специальных возможностей,
которые не описаны в termcap). Это значит, что вы можете создавать
терминально-независимые программы. При этом вам нет необходимости изменять
все специфические обращения к терминалу, такие как ESC-последовательности
(символы, указывающие терминалу, что передаваемые после
них символы (символ) должны интерпретироваться как управляющие коды).
Это символы (\E) для терминала vt52 и (^) для Apple.
Наилучший пример - способ использования файла termcap редактором
vi. Он начинает выполнять указанную ему функцию, например движение
курсора, после чего ставит вопрос: "Какой код функции, которую мы хотим
выполнить?". Затем он ищет соответствующую последовательность в
той информации, которую предоставляет termcap.
С другой стороны, иногда вам необходимо оптимизировать какую-либо
функцию по скорости, заставив ее посылать коды непосредственно на определенный
терминал. В этом случае вам опять-таки полезен файл
termcap, поскольку вы можете найти необходимую информацию в соответствующем
файле termcap, после чего закодировать эту информацию в
вашей программе. Это мы и делаем в первой инструментальной программе
данной главы - программе 'c'.

ИМЯ: c

c Быстрая очистка экрана
НАЗНАЧЕНИЕ
Выводит последовательность символов очистки экрана с использованием
быстрой программы на языке Си. Код очистки, указанный в тексте
программы, следует изменить в соответствии с используемым терминалом.
ФОРМАТ ВЫЗОВА
c
ПРИМЕР ВЫЗОВА
c Очистка экрана
ТЕКСТ ПРОГРАММЫ
1 char id[] = "@(#) c v1.0 Fast clear screen Author: Russ Sage";
Быстрая очистка экрана
3 #define FF "\014"
5 main()
6 {
7 if (write(1, FF, 1) == -1)
8 write(2,"c: write error to stdout\n",25);
ошибка записи в стандартный вывод
9 }
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖНА ПРОГРАММА c?
В System V уже имеется команда для очистки экрана терминала - это
команда clear. Она работает путем определения типа вашего терминала и
затем вывода на экран символа очистки для данного терминала. Все прекрасно,
но есть один существенный недостаток: она очень МЕДЛЕННАЯ!
Мы же хотим как можно быстрее выполнить очистку экрана. Самой
быстрой операцией ввода-вывода в системе является прямой системный вызов
для чтения или записи. Мы применяем этот вызов, а также выполняем
небольшую проверку ошибок для определения доступности стандартного
устройства вывода.
ЧТО ДЕЛАЕТ c?
Программа 'c' выводит на экран символ очистки настолько быстро,
насколько быстро может выполняться операция ввода-вывода в UNIX. Применяя
прямой системный вызов, мы избавляемся от необходимости запускать
другую программу. Поэтому программа 'c' работает очень быстро.
Мы уверены, что точно такую же функцию можно вызывать как команду
Си-shell (поместить в csh alias), поэтому данная программа наиболее
полезна тем, кто работает в System V.
Для того чтобы определить, какой символ очистки соответствует вашему
терминалу, найдите строку с обозначением cl в файле termcap. Это
и есть то значение, которое вы должны вручную вставить в данную программу.
Если вы работаете не на таком терминале, для которого эта программа
написана, то данная команда будет работать неверно.
ПОЯСНЕНИЯ
Первым делом мы должны найти в файле termcap код очистки экрана.
Для терминала Apple это код ^L, а для vt52 это \EH\EJ. Как только вы
найдете этот код, вставьте его в оператор define в строке 3 или сразу
в оператор write в строке 7. В приведенном примере в качестве символа
очистки экрана используется ^L.
Наиболее быстрым способом передачи символа в файл является непосредственное
выполнение оператора write. Поскольку терминалы являются
файлами, мы можем выполнять запись непосредственно в них, пользуясь
преимуществом предопределенных дескрипторов файла 0,1 и 2.
Системный вызов write в строке 7 посылает символ очистки в файл с
дескриптором 1, который является стандартным устройством вывода. Если
операция записи неудачна (по ряду причин), то в файл с дескриптором 2,
т.е. на стандартное устройство регистрации ошибок, выводится сообщение
об ошибке. Здесь не проверяется, успешно ли завершилась запись на
стандартное устройство регистрации ошибок. Если ошибка все-таки возникнет,
то мы ее увидим.
Программа не использует НИКАКИХ возможностей стандартного ввода-вывода
(stdio). НИКОГДА нельзя смешивать системные вызовы ввода-вывода
(т.е. вызовы из раздела (2) документации по системным функциям,
например read или write) со стандартными вызовами ввода-вывода (т.е.
вызовами из раздела (3), такими как getchar и printf). Дополнительный
буфер, который создается при выполнении функций stdio, не согласован
во времени с системными вызовами, поэтому все выходные сообщения перемешиваются.
Еще один аспект, о котором мы должны помнить, принимая решение об
использовании системных вызовов, это преимущество получения как можно
более короткого объектного кода. Небольшая программа загружается и работает
быстрее. Для того, чтобы ненужные подпрограммы стандартного
ввода-вывода не включались в наш объектный модуль, в исходном тексте
программы не делается никаких ссылок на подпрограммы stdio. Тем не менее,
ваша система могла их каким-то образом включить. Так поступает
XENIX, а вместе с stdio вызывается malloc и все остальное. Вы можете
просмотреть таблицу символов вашего объектного модуля с помощью nm(1)
или nlist(2). Вы увидите весь мусор, который был добавлен в ваш объектный
модуль. Не так редко мы получаем 6 Кб кода всего лишь для одного
оператора printf! Приучайтесь программировать непосредственно на
ассемблере, чтобы достичь того, что вам нужно.
ИССЛЕДОВАНИЯ
Когда эта программа была написана, возник вопрос: "Каким образом
мы можем проверить неудачу записи на стандартное устройство вывода?".
Раньше такой вопрос не стоял, но показалось, что неплохо было бы это
сделать. Решение было найдено на страницах описания sh(1). Способ, которым
можно вызвать ошибку выполнения записи на стандартное устройство
вывода, заключается в том, что нужно закрыть дескриптор файла стандартного
устройства вывода. Это легко делается с помощью команды exec,
которая является внутренней по отношению к shell:
$ exec "&-
Эта команда переназначает файловый дескриптор 1 стандартного вывода
(обозначение ") на дескриптор файла (&) закрытого устройства (-).
Такой эксперимент может оказаться полезным для более полной отладки
ваших программ.
ДИСКОВЫЕ УСТРОЙСТВА
К дисковым устройствам относятся гибкие и жесткие диски. Каждый
диск может быть разделен на одну или несколько частей, каждая из которых
связана с файлом устройства.
Основное отличие между дисками и терминалами заключается в том,
что диски являются блочными устройствами, а терминалы - символьными.
Вместо того, чтобы выполнять обмен информацией по одному символу,
диски обмениваются блоками по 512 или 1024 символа. Имеются команды,
которые управляют разбиением на блоки и буферизацией, что делает возможным
выполнение блочных операций ввода-вывода.
РАЗБИЕНИЕ ДИСКОВ НА РАЗДЕЛЫ
Части, или области диска, известны как разделы. Раздел может содержать
файловую систему, которая сгенерирована командой mkfs(1), или
же может содержать неструктурированные данные, доступ к которым выполняется
с помощью команды 'cpio -o'.
В системе XENIX управление разделами осуществляется программой
fdisk, которая концептуально подобна своей тезке в системе MS-DOS. В
других системах UNIX используются другие имена. Например, в системе
AT&T 7300 UNIX PC используется программа iv, что значит "format" (хотите
верьте, хотите нет). Как упоминалось ранее, обычно разделы содержат
одну файловую систему. В настоящее время в системах XENIX и SCO
XENIX у вас есть возможность "разделить раздел" на более мелкие части
для получения большего количества файловых систем. Это сделано по той
причине, что машины с системами DOS и XENIX ограничены четырьмя дисковыми
разделами, а у вас может возникнуть желание иметь больше файловых
систем, чем число доступных разделов. В системе AT&T 7300 UNIX PC управление
разделами диска осуществляется по списку начальных номеров
дорожек. Вы можете создать столько разделов, сколько хотите. Каждый
компьютер имеет свои преимущества и недостатки.
В каталоге /dev находятся имена как блочных устройств, так и символьных.
По этим именам вызываются различные драйверы устройств. Ниже
приводится пример списка интерфейсов жестких дисков.

|
| brw------- 1 sysinfo sysinfo 1, 0 Feb 18 17:07 /dev/hd00
| brw------- 1 sysinfo sysinfo 1, 15 Feb 18 16:59 /dev/hd01
| brw------- 1 sysinfo sysinfo 1, 23 Feb 18 16:59 /dev/hd02
| brw------- 1 sysinfo sysinfo 1, 31 Feb 18 16:59 /dev/hd03
| brw------- 1 sysinfo sysinfo 1, 39 Feb 18 16:59 /dev/hd04
| brw------- 1 sysinfo sysinfo 1, 47 Feb 18 17:07 /dev/hd0a
| brw------- 1 sysinfo sysinfo 1, 55 Feb 18 17:09 /dev/hd0d
| crw------- 1 sysinfo sysinfo 1, 0 Feb 18 16:59 /dev/rhd00
| crw------- 1 sysinfo sysinfo 1, 15 Feb 18 16:59 /dev/rhd01
| crw------- 1 sysinfo sysinfo 1, 23 Feb 18 16:59 /dev/rhd02
| crw------- 1 sysinfo sysinfo 1, 31 Feb 18 16:59 /dev/rhd03
| crw------- 1 sysinfo sysinfo 1, 39 Feb 18 16:59 /dev/rhd04
| crw------- 1 sysinfo sysinfo 1, 47 Feb 18 16:59 /dev/rhd0a
| crw------- 1 sysinfo sysinfo 1, 55 Feb 18 17:09 /dev/rhd0d
|
Имена файлов с префиксом hd указывают блочные устройства, а с
префиксом rhd - "неструктурированные" символьные устройства. Не все
символьные устройства являются неструктурированными блочными устройствами.
Терминалы являются символьными устройствами, как мы уже видели
ранее в данной главе. В табл. 7-2 показаны различные характеристики
этих двух типов устройств.
Таблица 7-2
Сравнение блочных и символьных устройств

Блочное устройство Символьное устройство

/dev/hd0, /dev/fd0 /dev/rhd0, /dev/rfd0
буфер управляется ядром буферизация отсутствует,
системы, медленное устройство быстрое устройство
произвольное размещение последовательное размещение
блоков данных блоков данных
доступ через файловую доступ непосредственно
систему на диск
cpio -p cpio -o, -i
mkfs, mount, df, du tar
fsck, fsdb

Как видите, существует много способов работы с устройствами.
Давайте рассмотрим устройство /dev/hd01 из приведенного выше
списка. Если вы хотите адресоваться к физическому разделу на диске как
к блочному устройству, вы можете создать на нем файловую систему. Для
этого вам нужно выполнить следующую команду, которая создаст файловую
систему размером 5000 Кб (5 Мб) на жестком диске:
# mkfs /dev/hd01 5000
Внутри раздела (размером не менее 5000 Кб) размещается файловая
система. Файловая система содержит суперблок, списки свободных блоков
и т.п., то есть все, что необходимо для хранения файлов, которые размещаются
здесь. Однако, создание файловой системы совсем НЕ означает,
что вы сразу же можете получить к ней доступ. Сначала вам необходимо
смонтировать файловую систему. Команда для выполнения этой операции
может иметь такой вид:
# mount /dev/hd01 /mount_pt
Файлы могут быть помещены в дисковый раздел командами mv или cp,
путем переадресации вывода в каталог с этим именем, например,
"/mount_pt/file.
Для использования раздела диска в качестве области неструктурированных
данных, а не блочного устройства, применяйте файл с именем символьного
устройства, которое начинается с буквы r. Например, для
использования того же устройства, что и в предыдущем примере, в качестве
неструктурированного устройства, укажите имя /dev/rhd01. (Из
списка устройств вы видите, что это символьное устройство, так как
права доступа в первой колонке начинаются с символов crw, а не brw).
Это устройство (и соответствующий раздел) в данный момент не имеет
файловой системы и является просто набором байтов. Единственным ограничением
является то, что вы можете записать в этот раздел не более 5
Мб данных.
Вот пример команды, использующей неструктурированное устройство:
$ find . -print | cpio -ocBv " /dev/rhd01
ИЗУЧЕНИЕ ДАННЫХ
Когда данные находятся на диске, их можно изучить более тщательно,
чем с помощью команд cat, more и других. Делается это командой
od(1), которая выдает дамп файла устройства, как показано в следующем
примере:
$ od -c /dev/hd01
Если бы вы получали дамп файла НЕСТРУКТУРИРОВАННОГО устройства
(/dev/rhd01), то это выглядело бы точно так же. Единственное отличие
заключается в том, как драйвер осуществляет доступ к данным. Формат, в
котором будут выводиться данные, зависит от того, какой командой производилось
копирование: cpio, tar, mkfs или какой-то иной. Некоторые
другие способы получения данных с устройства:
$ cat /dev/hd01
$ cat " /dev/hd01
$ tail /dev/fd0
Если вы дампируете файл устройства, содержащего файловую систему,
то данные будут представлять собой неупорядоченные блоки по 512 байт.
В одном месте вы можете увидеть списки каталогов. Другими словами, одно
и то же устройство может рассматриваться двумя совершенно разными
способами: как файловая система и как набор неструктурированных битов.
Хотя выполнение чтения двумя этими способами может быть поучительным,
в большинстве случаев у вас не возникнет желания выполнить ЗАПИСЬ информации
на одно и то же устройство двумя способами, поскольку, например,
неструктурированное устройство не будет ничего знать о файловой
системе в данном разделе и может затереть данные, относящиеся к файловой
системе.
Теперь, когда вы знаете, как осуществить доступ к диску, мысленно
вернемся к главе 2 и программам копирования. Командный файл cpiobr
использует для копирования файлов неструктурированное дисковое устройство
/dev/rfd0, в то время как autobkp использует файловую систему.
Большинство из этих способов работы с устройствами могут показаться
несколько экзотичными и предназначенными в основном для шутки и
обучения. Однако часто шутка помогает продуктивно работать. Ведь пытаясь
заставить систему сделать то или иное, вы можете открыть для себя
новые возможности системы. Ситуация с аппаратурой очень похожа. Появляются
новые устройства, и требуются годы для разработчиков программного
обеспечения, чтобы обнаружить все возможности машины. Система
UNIX существует в том или ином виде уже более десяти лет, но пользователи
до сих пор открывают ее новые и удивительные способности.
Итак, поскольку вы обычно должны выбрать тот или иной метод
использования раздела диска, то ничто не мешает вам завести на устройстве
все разделы одинакового типа. Обычным подходом является создание
файловых систем во всех возможных разделах, чтобы они могли содержать
файлы. Тем не менее, вы можете сочетать файловую систему с "неструктурированными"
разделами любым способом, который вам нравится. Одной
из возможных схем является использование одного раздела (fd01) в
качестве неструктурированного устройства для копирования файлов командой
"cpio -o". Этот раздел занимает почти весь диск, но какая-то часть
отводится для размещения второго раздела с файловой системой (fd02).
Распределенное пространство содержит некоторые справочные (help) файлы
и текстовый файл с именами файлов, находящихся в неструктурированном
разделе. Такое разбиение на разделы использует преимущества обоих
способов. Для того чтобы получить данные, скопированные командой cpio,
вы вводите команду "cpio -i " /dev/rfd01". Для получения данных из
второго раздела, вы вводите команду "mount /dev/fd02 /mnt", а затем
используете команды ls, file, grep и другие, которые относятся к файловой
системе. В этом случае раздел с файловой системой служит для документирования
неструктурированного раздела.
ЗАГРУЖАЕМЫЙ ДИСК И АВТОНОМНЫЙ shell (SASH)
Инсталляция системы UNIX на жесткий диск обычно выполняется с помощью
автономного shell (SASH, standalone shell). Иногда эта операция
выполняется с магнитной ленты, но легче всего использовать гибкий
диск. Возникает вопрос: "Как загрузить UNIX с гибкого диска?"
Картина следующая: гибкий диск имеет один раздел или даже может
быть разделен на корневой раздел и раздел пользователей. В любом случае
гибкий диск имеет файловую систему, созданную другой системой и
помещенную на диск. Первый блок файловой системы является загружаемой
записью, которая размещается на носителе с помощью команды dd. Команда
dd копирует байты, начиная с самого начала устройства. Загрузочная запись
содержит код, необходимый для запуска системы UNIX с диска.
Второй блок - это суперблок, своего рода главный каталог файловой
системы. В нем находятся индексные дескриптооы файлов, содержащие информацию
о каждом файле, а также список доступных свободных блоков.
Корневая файловая система имеет также вариант ядра для гибкого диска,
который загружается и запускает shell точно так же, как это делает его
старший брат (ядро системы для жесткого диска) для всей системы в целом.
Вы даже можете смонтировать инсталляционный диск на жесткий диск
с другой системой и выполнять команды копирования. Ограничивающим фактором
является размер одного инсталляционного диска. Самый большой
объем гибкого диска на машинах PC - 1.2 Мб (используется на PC AT),
что вполне достаточно. Можно уместить почти всю программу загрузки,
которая необходима для запуска многопользовательской системы с гибкого
диска.
Как только ядро системы с гибкого диска загружено, имеется полная
файловая система со всеми файлами устройств. Ядро монтирует раздел
жесткого диска (предполагается, что жесткий диск был разбит на разделы)
и копирует на него файлы в формате файловой системы. Вот как это
выглядит:
# mount /dev/hd01 /mnt "-вызов с гибкого диска для монтирования
первого раздела жесткого диска
# copy /unix /mnt "-копирование ядра жесткого диска в раздел
жесткого диска
ОБНОВЛЕНИЕ ФАЙЛОВОЙ СИСТЕМЫ
Мы описали суперблок как запись с ключевой информацией о размере
и содержимом файловой системы. Причиной разрушения файловой системы
обычно являются проблемы, возникающие в суперблоке. Команда sync(1)
выполняет запись образа суперблока на диск, тем самым обновляя его.
Иногда эта операция должна выполняться автоматически и постоянно для
того, чтобы образы суперблока на диске и в памяти были одинаковы. В
настоящее время в System V включена программа update, которая запускается
из загрузочного файла /etc/rc. Она живет в системе и исполняет
команды sync и sleep. В результате информация о состоянии файловой
системы на диске хранится со всеми текущими изменениями, произведенными
с самой файловой системой. Если у вас нет такой программы, вы можете
написать командный файл на языке shell, которая работает в цикле,
вызывая команду sync через соответствующие интервалы команды sleep.
Запустите этот командный файл в фоновом режиме, чтобы поддерживать целостность
файловой системы.
МОНТИРОВАНИЕ ФАЙЛОВЫХ СИСТЕМ
Давайте рассмотрим, что происходит, когда файловая система монтируется
в древовидной структуре системы. На рис. 7 -3 показано, как
взаимодействуют между собой индексные дескрипторы (inodes) двух файловых
систем.
Рис. 7-3
Монтирование одной файловой системы в другую

+
+
|Раздел| Физическое| Логическое|
|диска | имя | имя |
|
|
| 1 | /dev/hd01 |/dev/root |
| 2 | /dev/hd02 |/dev/usr |
+
+
+----+
| |

Раздел 1 | / |inode 2 (ls -lia /)

/ / | | \ \ \
/ / +----+ \ \ \
/ / / | \ \ \ \
/ / / | \ \ \ \
/ / / | \ \ \ \
+---+ +---+ +---+ +---+ +-----+ +---+ +---+ +---+
|bin| |dev| |etc| |lib| |lost+| |mnt| |tmp| |usr| inode 245
| | | | | | | | |found| | | | | | +---+
+---+ +---+ +---+ +---+ +-----+ +---+ +---+ +-| |
/ | \ / | \ / |\ / | \ / | | \ | |
/ | \ +---+
/|\ /|\ /|\ /|\
|
/
+
/
КОМАНДА /
# /etc/mount /dev/hd02 /usr /
+-----+
| |

Раздел 2 | / | inode 2 (ls -lia /usr)

| |
+-----+ \ \ \
/ / / | \ \ \ \ \
/ / / | \ \ \ \ \
/ / / | \ \ \ \ \
/ / / | \ \ \ \ \
/ / / | \ \ \ \ \
+---+ +---+ +-------+ +---+ +-----+ +-----+ +-----+ +---+ +---+
|adm| |bin| |include| |lib| |lost+| |pre- | |spool| |sys| |tmp|
| | | | | | | | |found| |serve| | | | | | |
+---+ +---+ +-------+ +---+ +-----+ +-----+ +-----+ +---+ +---+
/ | \ / | \ / | | \ / | \ /|\ / || \ / | \
/ / | \ / | \ \
/ / | \ / | \ \
/|\ /|\ /|\ /|\ / | \ /|\ /|\ /|\

В примере, показанном на рис. 7-3, файловая система из раздела 2
монтируется в корневой файловой системе (раздел 1) в каталог /usr. Однако
мы помним, что каждая файловая система имеет свой собственный
корневой каталог. В каждой файловой системе нумерация индексных дескрипторов
файла начинается с числа 2, поэтому номера индексных дескрипторов
дублируются в двух файловых системах. Это и является причиной,
по которой не могут быть образованы связи между файлами, находящимися
в разных файловых си бразованы связи между файлами, находящимися
Одним из атрибутов корневого каталога является то, что номер его
индексного дескриптора равен 2. Это значение может быть проверено в
корневом каталоге командой "ls -lid /". Каталог /usr - это просто еще
один файл (а именно каталог) в корневой файловой системе. Этот каталог
может содержать файлы и подчиненные каталоги, которые хранятся в разделе
1. После выполнения команды "mount /dev/hd02 /usr" корневой каталог
раздела 2 (индексный дескриптор 2) помещается в каталог /usr (индексный
дескриптор 245). Если какие-либо файлы существуют в каталоге
/usr в разделе 1, они остаются там, но получить доступ к ним вы не можете.
Единственным способом увидеть их является размонтирование файловой
системы, которая была смонтирована на их место. Хитрость команды
mount заключается в том, что она представляет новый раздел как бы принадлежащим
реальному корневому разделу. В сущности, это позволяет
иметь безграничную файловую систему.
Механизмом, который позволяет производить это, является таблица
смонтированных устройств, находящаяся внутри ядра системы. Когда выполняется
обращение к файлу, его индексный дескриптор определяет маршрут,
по которому находится данный файл. Если в таблице смонтированных
устройств имеется запись, то этот маршрут ведет на другой раздел диска
или в другую файловую систему. Для того чтобы убедиться, что вновь
смонтированная файловая система уникальна, посмотрите индексный дескриптор
каталога /usr сначала из корневого каталога (командой "ls -li
/", индексный дескриптор 245), а затем из другой файловой системы (ls
-ldi /usr, индексный дескриптор 2).
КАК ПОЛУЧИТЬ ПОБОЛЬШЕ ИНФОРМАЦИИ О ФАЙЛОВОЙ СИСТЕМЕ?
Как указывалось ранее, файловая система размещается внутри раздела
на диске. Файловые системы создаются командой mkfs(1), поддерживаются
командой fsck(1), отлаживаются командой fsdb(1), а первый доступ
к ним осуществляется командой mount(1). Каталог /usr/include содержит
все включаемые файлы для использования в программах на языке Си, реализующих
эти команды. Таким образом, этот каталог представляет собой
прекрасную возможность для поиска информации о файловой системе,
поскольку включаемые файлы содержат глобальные определения, используемые
подпрограммами файловой системы. В документации Bell Labs (в руководстве
программиста) также описаны некоторые внутренние таблицы,
используемые файловой системой.
Теперь мы готовы рассмотреть программные средства для автоматизации
рутинной работы с файловой системой.

ИМЯ: mntf

mntf Монтирование и размонтирование гибкого диска
НАЗНАЧЕНИЕ
Монтирует и размонтирует устройство гибкого диска в каталоге как
файловую систему с возможностью записи/чтения или только чтения.
ФОРМАТ ВЫЗОВА
mntf [-d] [-h] [-l] [-r] [-s]
Опции:
-d размонтирование гибкого диска из корневой
файловой системы
-h использование устройства с высокой плотностью
записи (а не с низкой)
-1 использование устройства 1, а не устройства 0
-r монтирование гибкого устройства как файловой
системы с возможностью только чтения
-s использование имен устройств, принятых в System V
По умолчанию выполняется монтирование гибкого диска 0 в каталог
/mnt.
ПРИМЕР ВЫЗОВА
mntf -d -1
Размонтирование гибкого диска на устройстве 1.
ТЕКСТ ПРОГРАММЫ
1 :
2 # @(#) mntf v1.0 Mount floppies Author: Russ Sage
Монтирование гибких дисков
4 CMD="/etc/mount"
5 DIR="/mnt"
6 DRIVE="0"
7 DENSITY="48ds9"
8 SYSTEM="xenix"
10 if [ $# -gt 0 ]
11 then for ARG in $*
12 do
13 case $ARG in
14 -d) CMD="/etc/umount"
15 DIR="";;
16 -h) DENSITY="96ds15";;
17 -1) DRIVE="1"
18 if [ -d /mnt1 ]
19 then DIR="/mnt1"
20 else echo "the directory /mnt1 does not exist" "&2
нет каталога /mnt1
21 echo "using the directory /mnt instead" "&2
используется каталог /mnt
22 fi;;
23 -r) DIR="$DIR -r";;
24 -s) SYSTEM="sysv";;
25 *) echo "mntf: invalid argument $ARG" "&2
26 echo "usage: mntf [-d] [-h] [-1] [-r] [-s]" "&2
27 echo " -d dismount" "&2
28 echo " -h high density" "&2
29 echo " -1 use drive 1" "&2
30 echo " -r read only" "&2
31 echo " -s System V device" "&2
32 echo " default: mount XENIX drive 0 48 tpi to " "&2
33 echo " /mnt as a read/write filesystem" "&2
34 exit 1;;
35 esac
36 done
37 fi
39 case $SYSTEM in
40 sysv) $CMD /dev/fp${DRIVE}21 $DIR;;
41 xenix) $CMD /dev/fd${DRIVE}${DENSITY} $DIR;;
42 esac
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ
CMD Основная команда, подлежащая выполнению
DIR Каталог, в котором производится монтирование устройства
DENSITY Плотность записи в виде, указанном в имени устройства
DRIVE Номер устройства, начиная с 0
SYSTEM Тип имени устройства, принятый в UNIX'е
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ mntf?
В машинах с гибким диском это устройство часто используется в
повседневных операциях. Оно применяется в качестве источника при
инсталляции системы и как обычный носитель для операций копирования.
Гибкие диски можно использовать в системе UNIX двумя способами.
Первый является неструктурированной последовательностью байтов, что
полезно для копирования магнитных лент и хранения архивов. Второй
способ - поблочный, ориентирован на поддержку файловой структуры. Для
второго способа существует мощная поддержка на уровне файловой системы,
но некоторые функции мы должны реализовать самостоятельно.
Для того чтобы использовать гибкий диск как файловую систему в
UNIX, вам необходимо подготовить диск и смонтировать его как файловую
систему. Когда вы закончите работу, вы должны размонтировать гибкий
диск. Это отличается от системы DOS, в которой гибкие диски можно
вставлять и вынимать когда угодно, если только в этот момент на них не
идет запись.
Поскольку использование гибких дисков включает в себя взаимосвязанные
шаги монтирования и размонтирования, то было бы вполне естественным
применять одну команду с соответствующими опциями для выполнения
монтирования и размонтирования. Однако UNIX так не делает. Наш командный
файл mntf объединяет эти две функции в одной команде для упрощения
работы с гибким диском. Для того чтобы сделать нашу программу
более независимой, мы предусмотрели в ней поддержку устройств системы
XENIX наравне с устройствами System V. (Системы Berkeley (BSD) не так
часто используют гибкие диски, поэтому мы не пытались иметь с ними дело.)
ЧТО ДЕЛАЕТ mntf?
Эта программа обеспечивает поддержку всех возможностей для монтирования
и размонтирования гибких дисков. Она предоставляет все опции,
необходимые команде mount, акцентирует внимание на тех аспектах файловой
системы, которые относятся к гибким дискам, и уменьшает количество
нажатий на клавиши, необходимых для выполнения этой работы.
Действие программы по умолчанию заключается в монтировании гибкого
диска низкой плотности записи, находящегося в устройстве 0, в каталог
/mnt. Имеется много опций, чтобы попросить программу mntf сделать
то, что вам нужно. Опция -h поддерживает диск высокой плотности (1.2
Мб). В машинах PC AT первое из устройств гибких дисков имеет 96 дорожек
на дюйм, объем 1.2 мегабайта, но может также читать и писать гибкие
диски с более низкой плотностью. Второй гибкий диск является устройством
низкой плотности с 48 дорожками на дюйм и объемом 360 килобайт.
Опция -1 (цифра один, а не буква l) выполняет монтирование гибкого
диска в устройстве 1, а не 0. Опция -r монтирует файловую систему с
возможностью ТОЛЬКО ЧТЕНИЯ. Для РАЗМОНТИРОВАНИЯ диска вместо монтирования
используется опция -d. Если применяется опция -s, имя устройства
изменяется таким образом, чтобы оно соответствовало системе System V,
а не XENIX. Это незначительная проблема, поскольку схемы именования не
очень отличаются. Данная программа создана для системы XENIX и обеспечивает
наилучшие возможности именно в ней.
Не все опции совместимы друг с другом, но проверка на совместимость
не выполняется. Например, команда "mntf -d -r" пытается размонтировать
файловую систему с возможностью только чтения, а команда UNIX
unmount, которая выполняет эту операцию, отбрасывает ее, выдавая сообщение
об ошибке. В целях упрощения мы отказались от проверки соответствия
опций, а вместо этого предоставили UNIX'у право выдавать
сообщения об ошибках для информирования пользователя о возникших проблемах.
Если вы хотите, чтобы эту программу мог применять относительно
неопытный пользователь, вам нужно вставить в нее выполнение таких проверок.
ПРИМЕРЫ
1. $ mntf -s
Монтирование гибкого диска как файловой системы с возможностью
записи-чтения и с использованием имен устройств, принятых в System V.
2. $ mntf -h -1 -r
Монтирование гибкого диска высокой плотности записи на устройстве
1 как файловой системы с возможностью только чтения и с использованием
формата имен устройств, принятого в XENIX. Эта команда должна закончиться
неудачей (устройство 1 имеет низкую плотность).
3. $ mntf -d -h
Размонтирование файловой системы на устройстве 0 с высокой плотностью
записи и с использованием имен устройств, принятых в XENIX.
ПОЯСНЕНИЯ
Для того чтобы максимально упростить программу, все фактически
выполняемые команды помещены в текстовые строки. Это позволяет достичь
большей гибкости при написании программы. Результатом анализа командной
строки является формирование команды, которая выполняется в конце
программы mntf.
В строках 4-8 инициализируются установки по умолчанию. Переменная
CMD содержит команду UNIX, которая в итоге должна быть выполнена, по
умолчанию это команда mount. Переменная DIR указывает каталог, в который
должно быть смонтировано устройство, по умолчанию это каталог
/mnt. Переменная DRIVE является номером устройства (по умолчанию 0) и
используется для формирования корректного имени устройства. Переменная
DENSITY по умолчанию установлена для носителя низкой плотности, т.е.
48 дорожек на дюйм, двусторонняя дискета с 9 секторами на дорожку
(48ds9).
В строке 10 проверяется, указаны ли в командной строке какие-либо
аргументы. Если количество аргументов больше нуля, последовательно
проверяется каждый аргумент. Если какой-либо из аргументов соответствует
образцам в строках 13-35, то он изменяет содержимое командной
строки.
Строка 14 управляет опцией -d для размонтирования гибкого диска.
Переменная CMD изменяется на umount вместо mount. После этого переменной
DIR присваивается нулевое значение, поскольку команде umount требуется
не каталог, а только имя устройства. Переменная DIR должна быть
частью строки с командой для того, чтобы мы могли использовать одну и
ту же "заготовленную" командную строку для всех вариантов. В данном
случае мы устанавливаем эту переменную в нуль, а shell при синтаксическом
разборе удаляет ее из командной строки.
В строке 16 выполняется изменение плотности записи используемого
носителя. Обращение к различным типам носителей выполняется по именам
файлов устройств. Каждое имя указывает драйвер устройства, который раоснованную
на утилите at, и возможность запуска еще одного командного
жет работать в режимах как высокой, так и низкой плотности записи. Однако
если вы укажете имя устройства с высокой плотностью записи, а на
самом деле оно имеет низкую плотность, то драйвер работать не будет
из-за ошибок чтения.
ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ ОБ ИМЕНАХ УСТРОЙСТВ
Ниже приводится список, полученный командой ls в каталоге dev для
машины XT, в которой нет устройств высокой плотности записи. Этот
список позволяет проиллюстрировать, каким образом осуществляется обращение
к именам устройств:

|
| 32 brw-rw-rw- 3 bin bin 2, 4 Jun 25 09:25 /dev/fd0
| 32 brw-rw-rw- 3 bin bin 2, 4 Jun 25 09:25 /dev/fd048
| 126 brw-rw-rw- 1 root root 2, 12 Feb 18 17:09 /dev/fd048ds8
| 32 brw-rw-rw- 3 bin bin 2, 4 Jun 25 09:25 /dev/fd048ds9
| 125 brw-rw-rw- 1 root root 2, 8 Feb 18 17:09 /dev/fd048ss8
| 127 brw-rw-rw- 1 root root 2, 0 Feb 18 17:09 /dev/fd048ss9
| 131 brw-rw-rw- 3 root root 2, 5 Feb 18 17:09 /dev/fd1
| 131 brw-rw-rw- 3 root root 2, 5 Feb 18 17:09 /dev/fd148
| 129 brw-rw-rw- 1 root root 2, 13 Feb 18 17:09 /dev/fd148ds8
| 131 brw-rw-rw- 3 root root 2, 5 Feb 18 17:09 /dev/fd148ds9
| 128 brw-rw-rw- 1 root root 2, 9 Feb 18 17:09 /dev/fd148ss8
| 130 brw-rw-rw- 1 root root 2, 1 Feb 18 17:09 /dev/fd148ss9
|
Крайнее слева число представляет собой номер индексного дескриптора.
Мы используем его как ссылку для определения уникального имени
файла. Как мы уже отмечали ранее, несколько имен устройств могут относиться
к одному и тому же файлу, рассматриваемому с различных точек
зрения. Например, в данном списке вы видите, что три устройства имеют
индексный дескриптор 32. Второе число слева представляет собой количество
связей. Когда оно больше единицы, то это также указывает, что
несколько устройств являются на самом деле одним файлом, а следовательно
используют один и тот же индексный дескриптор. Следующие два
числа являются старшим и младшим номером. Старший номер относится к
драйверу устройства, а младший является уникальным номером одного из
устройств, управляемых одним и тем же драйвером.
Большинство из этих имен устройств соответствуют определенному
шаблону. Они состоят из символов fd (floppy disk - гибкий диск), цифры
0 или 1 (номер устройства), числа 48 (плотность, выраженная в виде количества
дорожек на дюйм), символов ss или ds (single-sided - односторонняя
или double-sided - двусторонняя дискета) и цифры 8 или 9 (число
секторов).
Мы видим по индексным дескрипторам, что устройство fd0 связано с
устройствами fd048 и fd048ds9. Самым информативным именем (и самым
трудным при вводе с клавиатуры) является имя fd048ds9. Оно точно выражает,
к какому устройству и типу носителя мы обращаемся. Для того чтобы
упростить указание этого имени, устройство fd048ds9 связывается с
более короткими именами. Все три имени файла являются корректными.
Следующий список получен на машине AT, в которой имеется устройство
высокой плотности:

|
| 102 brw-rw-rw- 3 bin bin 2, 7 Jun 17 14:28 /dev/fd0
| 95 br--r--r-- 2 bin bin 2, 3 Jun 6 09:23 /dev/fd048
| 93 br--r--r-- 1 bin bin 2, 2 Jun 6 09:23 /dev/fd048ds8
| 95 br--r--r-- 2 bin bin 2, 3 Jun 6 09:23 /dev/fd048ds9
| 92 br--r--r-- 1 bin bin 2, 0 Jun 6 09:23 /dev/fd048ss8
| 94 br--r--r-- 1 bin bin 2, 1 Jun 6 09:23 /dev/fd048ss9
| 102 brw-rw-rw- 3 bin bin 2, 7 Jun 17 14:28 /dev/fd096
| 102 brw-rw-rw- 3 bin bin 2, 7 Jun 17 14:28 /dev/fd096ds15
| 99 brw-rw-rw- 3 bin bin 2, 11 Jun 26 19:34 /dev/fd1
| 99 brw-rw-rw- 3 bin bin 2, 11 Jun 26 19:34 /dev/fd148
| 97 br--r--r-- 1 bin bin 2, 10 Jun 6 09:23 /dev/fd148ds8
| 99 brw-rw-rw- 3 bin bin 2, 11 Jun 26 19:34 /dev/fd148ds9
| 96 br--r--r-- 1 bin bin 2, 8 Jun 6 09:23 /dev/fd148ss8
| 98 br--r--r-- 1 bin bin 2, 9 Jun 6 09:23 /dev/fd148ss9
| 103 brw-rw-rw- 2 bin bin 2, 15 Jun 6 09:23 /dev/fd196
| 103 brw-rw-rw- 2 bin bin 2, 15 Jun 6 09:23 /dev/fd196ds15
|
Если мы посмотрим на записи с индексным дескриптором 102, начиная
с середины списка, то увидим прогрессирующее упрощение имен по мере
продвижения к первой записи - устройству 0, которое имеет высокую
плотность записи по умолчанию. Для того чтобы обратиться к нему как к
устройству с низкой плотностью записи, необходимо использовать имя
fd048, а не fd0. Поскольку большинство используемых гибких дисков имеют
низкую плотность записи, то имя fd048ds9 является умолчанием в
программе mntf.
Строка 17 соответствует опции -1 для указания устройства 1 вместо
устройства 0. Строки 18-22 проверяют, сиществует ли каталог для монтирования
второго устройства. Если вы используете два гибких диска одновременно,
то вы не можете монтировать их оба в один и тот же каталог.
Для разрешения этой проблемы программа mntf использует для монтирования
устройства 1 каталог /mnt1, а не /mnt. Если каталог /mnt1 не
существует, по умолчанию используется каталог /mnt, и все хорошо, если
вы используете только устройство 1. Однако следует избегать монтирования
одного гибкого диска на место второго. Вы можете получить непредвиденные
результаты. Если вы собираетесь монтировать два гибких диска,
убедитесь, что у вас есть и каталог /mnt, и /mnt1.
Строка 23 делает монтируемую файловую систему доступной только
для чтения в случае, если была указана опция -r, что выполняется добавлением
символов -r к имени каталога. Это не является частью имени
каталога, но когда shell выполняет обработку команды, пробела между
именем каталога и -r достаточно, чтобы распознать -r как опцию.
Строка 24 соответствует опции -s и присваивает переменной SYSTEM
значение sysv. Это означает, что нужно использовать другие соглашения
об именах устройств.
Строки 25-34 выполняют проверку на ошибки в командной строке. Любая
опция, отличная от уже проверенных, является ошибкой, поэтому все,
что соответствует улавливающей ветке оператора case (*), считается недопустимой
опцией. В этом случае выводится синтаксическая подсказка, и
программа завершается.
В строках 39-42 выполняется вся основная работа. Оператор case
действует в соответствии со значением переменной SYSTEM. Если оно равно
"sysv", выполняется строка 40. В противном случае выполняется строка
41 для системы XENIX. Обратите внимание, что в нашей версии командного
файла mntf в строке sysv имеется только переменная с номером устройства.
Если вы используете System V, вы можете добавить переменную
для указания плотности записи или другие параметры, которые вам нужны.
Строка 41 выполняет версию команды, рассчитанную на систему
XENIX. Переменная CMD содержит, как мы отмечали, команду монтирования
(mount) или размонтирования (umount). Последовательность символов
/dev/fd указывает файл устройства для гибкого диска. Переменная DRIVE
равна 0 или 1. Переменная DENSITY указывает устройство с высокой или
низкой плотностью записи. Если должна быть выполнена команда монтирования,
переменная DIR содержит каталог. Если выполняется размонтирование,
значение переменной DIR равно нулю.
ЗАМЕЧАНИЕ ПО ВОПРОСУ БЕЗОПАСНОСТИ
Обычно только суперпользователь (root) может монтировать файловую
систему. В больших системах это имеет смысл. Однако на небольших
настольных машинах это может быть слишком ограничивающим фактором. Для
того чтобы обойти это требование, используйте возможность изменения
прав доступа. Чтобы позволить любому пользователю выполнять команды
монтирования и размонтирования, примените следующие команды:
# chown root /etc/mount "- делает пользователя root владельцем
модуля /etc/mount
# chmod 4511 /etc/mount и дает возможность выполнять команду mount
всем пользователям
# chown root /etc/umount "- делает то же самое для команды
# chmod 4511 /etc/umount размонтирования
Эти команды облегчают всем пользователям работу с гибким диском,
но одновременно открывают огромную дыру в защите системы. Если кто-либо
уже проложил тропинку прав доступа на гибкий диск (см. главу 9), то
монтирование файловой системы продолжит эту тропинку в главную систему
и позволит такому пользователю стать суперпользователем всей системы в
целом просто с гибкого диска!

ИМЯ: mntlook

mntlook Поиск файловых систем на устройствах
НАЗНАЧЕНИЕ
Просмотр всех файлов дисковых устройств и обнаружение всех файловых
систем, включая немонтированные.
ФОРМАТ ВЫЗОВА
mntlook
ПРИМЕР ВЫЗОВА
mntlook /dev/hd*
Поиск файловых систем на всех жестких дисках
ТЕКСТ ПРОГРАММЫ
1 static char id[] =
"@(#) mntlook v1.0 Look for mounts Author: Russ Sage";
Поиск файловых систем
3 #include
4 #include
5 #include
6 #include
7 #include
9 #define BSIZ 512
11 main(argc,argv)
12 int argc;
13 char *argv[];
14 {
15 struct filsys sb;
16 int d, dev;
17 char buf[BSIZ];
19 for (d = 1; d " argc; d++)
20 {
21 if (argv[d][0] == '-')
22 {
23 printf("mntlook: invalid argument %s\n", argv[d]);
24 printf("usage: mntlook device [device ...]\n");
25 continue;
26 }
27 if ((dev = open(argv[d],O_RDONLY)) " 0)
28 {
29 sprintf(buf, "cannot open %s",argv[d]);
невозможно открыть
30 perror(buf);
31 continue;
32 }
34 /* throw away first block */
обойти первый блок
35 if (read(dev, &sb, sizeof(sb)) == -1)
36 {
37 perror("cannot read block 0");
не читается блок 0
38 continue;
39 }
41 /* block 1 is the superblock */
блок 1 является суперблоком
42 if (read(dev, &sb, sizeof(sb)) == -1)
43 {
44 perror("cannot read block 1");
не читается блок 1
45 continue;
46 }
48 if (sb.s_magic == S_S3MAGIC)
49 {
50 printf("\nDEV: %s --" VALID file system\n",argv[d]);
51 printf("filsys: %s\n",sb.s_fname);
52 printf("pack : %s\n",sb.s_fpack);
53 printf("type : %s byte block\n",
54 (sb.s_type == S_B512) ? "512" : "1024");
55 printf("magic : %lx\n",sb.s_magic);
56 }
58 close(dev);
59 }
60 }
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖНА ПРОГРАММА mntlook?
Файловые системы являются сердцевиной системы UNIX. Сама система
не существует вне файловой системы, а расширенные возможности системы
обеспечиваются файловой системой.
Даже несмотря на то, что файловые системы настолько важны, UNIX
не имеет никаких средств для обнаружения файловых систем как таковых.
Не имеется даже средств, которые могли бы сообщить нам, какая информация
находится в суперблоках.
Нам необходимо универсальное средство для работы с файловыми
системами. Оно должно обнаруживать и идентифицировать суперблоки, находящиеся
на устройствах. Заметим, что операция открытия устройства
обычно требует наличия привилегий суперпользователя.
ЧТО ДЕЛАЕТ mntlook?
Программа mntlook предназначена для просмотра содержимого файлов
устройств и поиска суперблока. (Мы вкратце рассматривали суперблоки
ранее). Когда суперблок обнаружен, из него извлекается и выводится на
экран имя файловой системы, имя дискового пакета, используемый размер
блока и идентификационный "магический номер".
Первоначальной целью данной утилиты было обнаружение на внешнем
носителе машины таких файловых систем, которые в настоящий момент не
смонтированы. Но когда данная программа открывает и читает устройство,
ей все равно, было ли устройство смонтировано или нет, поскольку
доступ к нему выполняется на более низком уровне, чем уровень файловой
системы. В результате обнаруживаются все файловые системы, независимо
от того, были они смонтированы или нет.
Давайте рассмотрим, каким образом файловые системы связаны с физическим
носителем. К каждой машине подсоединено фиксированное число
периферийных устройств. Для работы со сменными носителями в UNIX реализована
концепция монтированного и немонтированного файлового пространства.
Но первым шагом является установка пакета дисков (или гибкого
диска) в дисковое устройство. После этого можно выполнять операции
чтения и записи на физических носителях, указывая определенные имена
устройств.
Монтирование представляет собой логическое действие, которое читает
суперблок с пакета дисков и записывает его в память, где ядро
системы поддерживает свою версию суперблока. Периодически выполняется
запись версии, находящейся в памяти, обратно на диск, чтобы эти две
версии были по возможности одинаковыми. В System V это делает программа
update, которая работает в фоновом режиме с момента загрузки системы.
Для обращения непосредственно к физическому носителю используются
такие команды, как, например, команда "od -c /dev /rfd0", которая дампирует
неструктурированный гибкий диск. Одной из команд, которые непосредственно
помещают данные на устройство, является команда "cp file
/dev/rfd0". Область данных начинается с самого первого байта на гибком
диске. Такие данные несовместимы с командами tar, cpio или файловой
системой.
Для обращения к файловой системе используется команда "mount
/dev/fd0 /mnt". Начиная с этого момента, все обращения к данному устройству
выполняются через каталог /mnt. Важно то, что непосредственный
доступ к файлу устройства является операцией более низкого уровня, чем
операции файловой системы, что позволяет прочитать информацию о суперблоке
непосредственно с устройства.
Входной информацией для программы mntlook являются имена файлов
устройств. В командной строке нельзя указывать никакие опции. Имена
устройств могут быть как блочными, так и символьными. Для операции
чтения это не имеет никакого значения, если только у вас имеются права
чтения. Затем программа читает второй блок и проверяет "магическое
число", определяющее суперблок. Суперблок - это просто структура языка
Си, которая предопределена системой и хранится в файле filsys.h, что
указано в нашей программе оператором #include. Магическое число
представляет собой длинное целое, имеющее заранее определенное значение.
Если элемент структуры, которая прочитана с диска, содержит это
число, то считается, что остальная часть структуры является корректными
данными. Для каждой файловой системы имеется только одна структура
суперблока.
Если магическое число такое, как надо, выводится остальная информация
о файловой системе. Если это число некорректно, на экран ничего
не выводится и программа обрабатывает в цикле следующий файл устройства,
указанный в командной строке.
Данная программа может служить в качестве функции, обеспечивающей
безопасность, поскольку она умеет идентифицировать файловые системы,
которые кто-либо оставил на машине несмонтированными. Как отмечается в
главе 9, нарушители защиты могут читать данные с немонтированных устройств,
поэтому если оставить устройство или файловую систему немонтированными,
то на самом деле это не предохраняет их от несанкционированного
доступа.
ПРИМЕРЫ
1. $ mntlook /dev/hd13
Поиск суперблока на устройстве с именем hd13. Это имя указывает
устройство 1, третий раздел. Для просмотра разделов в среде XENIX нужно
запустить программу fdisk. Для System V нужно воспользоваться командой
iv.
2. $ mntlook /dev/fd0*
Поиск файловых систем на гибких дисках с любой плотностью записи,
находящихся в устройстве 0. Это снова пример для системы XENIX.
3. $ for DEV in /dev/*[fh]d*
" do
" echo "checking device: $DEV"
" mntlook $DEV
" done
Данный цикл выполняется по всем именам устройств для гибих и
жестких дисков по очереди. Каждое имя выводится на экран. Затем проверяется,
содержит ли данное устройство файловую систему.
ПОЯСНЕНИЯ
Строки 3-7 определяют включаемые файлы, которые использует данная
программа. Вам необходимо изучить эти файлы, поскольку они не только
помогут вам понять работу этой программы, но и покажут вам некоторые
важные значения, имеющие отношение к файловым системам.
Строка 9 определяет размер используемого буфера. Этот буфер применяется
только для хранения сообщений об ошибках, поэтому он не должен
быть очень большим.
Строка 15 определяет структуру суперблока. Он имеет тип filesys
(см. включаемый файл sys/types.h). На моей машине суперблок имеет размер
1024 байта. Если вы не знаете точно размер буфера у вас, то вы можете
проверить его, вставив в программу следующий оператор:
printf ("the size is %d",sizeof(sb))
Строка 16 описывает рабочую переменную d и дескриптор файла dev.
Строка 17 объявляет буфер размером BSIZE.
Строки 19-59 - это один большой цикл for. Этот цикл представляет
собой всю остальную часть программы. Он выполняется столько раз,
сколько аргументов указано в командной строке. Счетчик цикла начинается
с 1, поскольку первым аргументом argv[0] является имя команды. В
качестве аргументов должны быть указаны имена файлов, поэтому данный
цикл использует каждое имя по очереди.
В строках 21-26 проверяется, не начинается ли текущий рассматриваемый
нами аргумент со знака '-'. Если да, то это опция, что
представляет собой ошибку, поэтому выводится сообщение об ошибке и
оператор continue вызывает выполнение следующей итерации цикла. Таким
образом, данная программа отвергает опции, но работает при обнаружении
имен файлов.
Считая, что имя файла было найдено, строки 27-32 открывают файл
устройства с этим именем только для чтения. Если данный вызов open неудачен,
мы посылаем сообщение в буфер вместе с именем, указанным в командной
строке. Этот буфер передается программе обработки ошибок
(системной), которая использует наше сообщение как первую часть своего
сообщения об ошибке. Она выводит системное сообщение, которое определяет
данную ошибку. По оператору continue начинается выполнение следующей
итерации цикла for.
Строки 35-39 читают первый блок файла. Для корневой файловой
системы первым блоком является загрузочная запись. Если при этом чтении
возникает ошибка, выводится сообщение об ошибке и выполнение цикла
продолжается.
Строки 42-46 читают второй блок, который должен находиться в том
месте, где размещается суперблок, если он имеется. По информации, прочитанной
и помещенной в структуру, мы можем получить доступ к каждому
элементу по его имени.
Строка 48 проверяет, равно ли магическое число в структуре магическому
числу, определенному в файле заголовка. Если они совпадают,
программа mntlook выводит имя файла устройства и сообщение о том, что
файловая система корректна, имя файловой системы (если оно имеется),
имя пакета дисков, размер используемого блока и магическое число в
шестнадцатиричном виде.
В строках 53-54 мы имеем подобие кодированной структуры: оператор
printf использует структуру типа if-then-else для указания строки, которую
нужно выводить. После того как выведена первая строка, выполняется
проверка и если она прошла успешно, то выводится значение 512.
Если в результате проверки получено значение "ложь", выводится значение
1024. Этот метод описан в книге B.W.Kernighan, D.M.Ritchie "The C
Programming Language" (Prentice-Hall, 1978).
Строка 58 закрывает устройство, и цикл возвращается в
начало для принятия следующего имени устройства.
РАЗМЕРНЫЕ ПАРАМЕТРЫ
Теперь, когда мы рассмотрели взаимоотношения между устройствами и
файловыми системами и некоторые параметры, связанные с форматами
дисков, давайте обратимся к гайкам и болтикам этих устройств. Хотя
основная часть этой информации может показаться экзотичной, она может
оказаться важной при определенных обстоятельствах. Например, для установки
системы UNIX на новую машину вам нужно разбить диск на сегменты
и понимать, каким образом UNIX фактически располагается на диске. Если
вы создаете программы, которые выполняют какую-либо операцию низкого
уровня с диском, вам, очевидно, необходимо понимать, что вы делаете.
Администраторам, поскольку они должны добавлять новые устройства в
систему, необходимо уметь определять количество файловых систем (т.е.
сколько можно создать разделов на диске), их размеры и знать, каким
образом установить файловые системы в эти разделы. Администраторы
должны также уметь писать или модифицировать драйверы устройств. Наконец,
при работе с дисками возникают проблемы, такие как плохие блоки,
которые необходимо изолировать и с которыми приходится иметь дело.
РАЗМЕРЫ БЛОКОВ
System V является последним достижением ветви фирмы AT&T в фамильном
дереве UNIX. Это означает, что System V содержит последние
правки, внесенные в исходную систему UNIX. Эти правки предназначены
для того, чтобы сделать UNIX жизнеспособным и стойким коммерческим
продуктом. Для повышения устойчивости были внесены изменения, касающиеся
работы с файлами и размеров их блоков.
Обычно обмен данными с дисками осуществляется блоками по 512 байтов.
Дисковая аппаратура имеет дело именно с таким размером. Для учета
этого факта UNIX первоначально использовал 512-байтные блоки внутри
файловой системы, что, возможно, облегчало написание программ и создавало
впечатление, что так и нужно. Однако нельзя отрицать, что при
этом UNIX может работать медленно!
Для ускорения работы внутренние программы в настоящее время
используют блоки размером 1024 байта. Сам диск должен выполнить два
обращения к 512-байтным блокам, но в системе две эти операции чтения
рассматриваются как одна операция чтения блока размером 1024 байта.
Единственная проблема заключается в том, что одни утилиты выдают результаты
в 512-байтных блоках, а другие - в 1024-байтных, в зависимости
от того, когда они были написаны. Когда сильно приближаются пределы
свободного пространства на диске, вам действительно нужно знать,
с каким размером вы имеете дело.
Для лучшего понимания проблемы размеров блоков, в табл. 7-3 показано,
какие утилиты какой размер блока используют. Эта информация получена
в основном из System V на машине VAX, из другого варианта
System V и из XENIX. Эти значения могут отличаться на разных машинах,
но идея сохраняется.
Вы видите, что большинство утилит выдают результат в блоках размером
512 байтов, но утилиты, относящиеся к файловой системе, выдают
результат в 1024-байтных блоках. Поскольку UNIX обращается к дисковому
пространству поблочно, важно уметь точно вычислять, сколько свободного
пространства в файловой системе. Весьма плоха ситуация, когда имеется
какой-то большой файл в редакторе vi (который использует файл /tmp для
промежуточного редактирования), а на диске недостаточно места для записи
временного файла редактора vi в реальный файл на диске. На самом
деле это может случиться на персональных машинах с ограниченным (скажем,
20 Мбайт) объемом жесткого диска.
Таблица 7-3
Размеры блоков для различных команд системы UNIX

512 байтов/блок 1024 байта/блок

ls -s fdisk (размеры разделов)
sum mkfs
cpio fsck
df
du

РАСЧЕТЫ, СВЯЗАННЫЕ С БЛОКАМИ
Еще одним важным вопросом, имеющим отношение к физическим устройствам
и логическим файловым системам, является определение местонахождения
определенного блока на жестком диске. Номер этого блока вычисляется
по номерам цилиндра, дорожки и сектора.
Знание номеров блоков становится важным, когда на диске появляется
дефектное место. Это дефектное место отмечается номерами цилиндра
и головки. Вам необходимо вычислить, какие блоки попадают в дефектную
область и занести их номера в таблицу дефектных блоков.
Обратная задача также важна. Если программа fsck начинает сообщать,
что где-то появился дефектный блок, то каким образом мы можем
узнать номера цилиндра, головки, сектора и т.д. для данного дефектного
блока? Такое обратное вычисление сделать очень тяжело, если не невозможно.
Во-первых, номер блока представляет собой произведение четырех
чисел. Трудно узнать, какие именно эти числа. Кроме того, файловые
системы могут использовать информацию вида база/смещение, поэтому блок
номер 1 в файловой системе в действительности является блоком номер
1382 на диске. Определить, какого вида информация была использована в
данном случае, тоже тяжело.
Конкретные данные в следующем примере относятся к вполне определенной
машине, но на других машинах используются подобные зависимости.
Эти данные относятся к машине с жестким диском объемом 20 Мбайт с
системами XENIX/DOS.
Характеристики устройства:
1 диск = 615 цилиндров, или 615 цилиндров/диск
1 цилиндр = 4 головки (дорожки), или 4 головки/цилиндр
Промышленный стандарт:
1 дорожка = 17 секторов, или 17 секторов/дорожку
1 сектор = 512 байт, или 512 байт/сектор
1 Кбайт = 1024 байта = 2^10
1 Мбайт = 1024 килобайта = 2^20 = 1 048 576 байт
Характеристики устройства различны для разных устройств, но промышленный
стандарт для числа секторов на дорожку и байтов на сектор
остается одинаковым. В табл. 7-4 показаны примеры характеристик различных
устройств.
Таблица 7-4
Размеры жестких дисков и их конфигурация

Число цилиндров Число головок Мегабайты

981 3 25
697 5 30
981 5 42
925 7 55
1024 8 71

Вы видите, что число цилиндров и число дорожек различны для устройств
с разным объемом. Определить максимальный объем дисковой памяти
можно перемножением всех чисел. В следующем примере вычисляется общий
размер в байтах для предыдущих данных.
615 цил 4 дор 17 сек 512 байт
------- * ----- * ------ * -------- = 21 411 840 байт/диск
1 диск 1 цил 1 дор 1 сек
21411840 байт 1 мегабайт
------------- * ------------ = 20.4 мегабайта/диск
1 диск 1048576 байт
Отметим, что если вы верно указываете единицы измерения, то они
попарно сокращаются, за исключением одной снизу и одной сверху, и в
результате получаются нужные единицы измерения в ответе. Таким образом,
в первой строке вычислений цилиндры, дорожки и секторы сокращаются,
и в качестве единиц измерения остаются байты/диск. Поскольку мы
имеем дело с таким большим количеством различных единиц измерения, вычисления
такого рода (иногда называемые "размерный анализ") убеждают
нас, что мы понимаем, о чем идет речь.
Объем доступной дисковой памяти уменьшается после форматирования,
локализации дефектных блоков и размещения на диске файловой системы.
Однако наш пример показывает, что все размеры согласуются между собой.
Важным моментом, на который необходимо обратить внимание, является
использование разных терминов. Иногда применяется число головок
на цилиндр, а иногда число дорожек на цилиндр. При использовании каждого
из этих терминов соответствующим образом изменяются и другие термины.
Существует такая взаимосвязь:
цилиндр, дорожка, сектор = физический сектор
цилиндр, головка, байт = блок
Эти две записи выражают в точности одно и то же. Когда вы используете
запись вида цилиндр/дорожка/сектор, то в результате получаете
физический сектор. Используя запись вида цилиндр/головка/байт, вы получаете
в результате номер блока. Следует помнить, что ГОЛОВКА - это
то же самое, что и ДОРОЖКА. Если вы это запомните, все остальное встанет
на свое место.
Ниже приводятся некоторые общие вычисления, которые часто всплывают,
когда вы работаете с диском на низком уровне. Эти примеры в
большей степени относятся к самому устройству, чем к системе UNIX. Однако
после того, как вы поймете эту информацию о диске, вам будет легче
понять, как работает UNIX на этом уровне.
1. Сколько дорожек имеет диск?
615 цил 4 дор
Решение: ------- * ----- = 2460 дор/диск
1 диск 1 цил
2. Сколько байт в дорожке?
17 сек 512 байт
Решение: ------ * -------- = 8704 байт/дор
1 дор 1 сек
3. Сколько дорожек в одном мегабайте?
2460 дор 1 диск
Решение: -------- * ------ = 123 дор/Мб
1 диск 20 Мб
4. Сколько цилиндров в одном мегабайте?
1 цил 2460 дор
Решение: ----- * -------- = 30 цил/Мб,
4 дор 20 Мб
615 цил 1 диск 123 дор
или ------- * -------- * ------- = 30 цил/Мб
1 диск 2460 дор 1 Мб
5. Дан цилиндр 47, дорожка 2, сектор 4. Какой физический номер секто-
ра?
Решение:
Сначала мы обращаем внимание на то, что вопрос касается секторов.
В качестве единиц измерения даны цилиндр, дорожка и сектор. Как перевести
их в другие единицы? Мы знаем, что головки - это то же самое,
что и дорожки, поэтому в вычислениях нужно использовать 4 головки
вместо 4 дорожек:
4 дор 17 сек 17 сек
47 цил * ----- * ------ + 2 дор * ------ + 4 сек =
1 цил 1 дор 1 дор
= 3196 сек + 34 сек + 4 сек =
= сектор 3234
РАЗМЕРЫ ФАЙЛОВ
В основном при работе в системе UNIX мы считаем, что ее ресурсы
безграничны. Например, мы не заботимся о том, что созданный файл получится
"слишком большим", а это не так уж редко в персональных компьютерах
на гибких дисках. Если же мы занимаемся сопровождением и администрированием
системы UNIX, то мы должны быть готовы иметь дело с ситуациями,
когда превышаются различные предельные значения системы.
Всегда лучше исследовать эти вопросы заранее в некритичных ситуациях,
поэтому давайте рассмотрим пределы размеров файлов и их смысл.
Некоторые параметры "зашиты" в ядро системы при ее генерации. Одним
из таких значений является максимальный размер файла. Он определяет
наибольшее число блоков, которые может занимать файл. Этот параметр
тесно связан с принятым в UNIX методом использования индексных дескрипторов
файла (inodes). Это наборы указателей, среди которых первые
десять указывают на блоки данных, следующий указывает на другую таблицу,
следующий - на таблицу, указывающую на таблицу и т.д.
Имеется еще одно ограничение размера файла, которое определено
для каждого пользователя во время работы в системе - число ulimit
(user limit - пользовательский предел). Это значение устанавливается в
момент вашей регистрации в системе и представляет собой число блоков
по 512 байт, которые вы можете записать в любой заданный файл. В
shell'е имеется команда ulimit, которая при ее вызове без аргументов
выводит это число. Эта же команда позволяет вам уменьшить ваше значение
ulimit. Только суперпользователь (root) может УВЕЛИЧИТЬ значения
ulimit.
Побочным эффектом уменьшения значения ulimit является то, что вы
не можете снова увеличить его до регистрационного значения. Значение
ulimit остается таким же на все время работы вашего shell, поэтому для
восстановления регистрационного значения вам необходимо выйти из
системы, а затем снова зарегистрироваться.
Еще одним интересным моментом является то, что если вы установите
ваше значение ulimit равным 0, вы не сможете создать никакие файлы!
Максимально допустимым размером файла в данном случае является нулевой,
поэтому никакой файл не может быть создан. Это представляется
достаточно резонным, однако существуют такие ситуации, когда файл нулевого
размера МОЖЕТ существовать. Опять же, для восстановления вашего
обычного значения ulimit необходимо выйти из системы, а затем снова
зарегистрироваться.
Как отмечалось ранее, увеличить значение ulimit может только суперпользователь.
Эта процедура довольно проста. Сначала нужно увеличить
значение ulimit командой ulimit, а затем запустить shell. Этот
новый shell имеет новое значение ulimit. Если мы хотим, чтобы система
загружалась с shell, имеющим большее значение ulimit, мы можем установить
программу в inittab (таблице инициализации системы), чтобы эта
операция выполнялась автоматически.
Ниже приводится пример программы, которая изменяет значение
ulimit и запускает shell с этим новым значением. Напомним, что эта
программа может быть запущена только суперпользователем.
1 #include
2 #include
4 main()
5 {
6 long v1, v2, v3, newlimit = 5120;
8 v1 = (long)ulimit(UL_GFILLIM, 0L);
9 v2 = (long)ulimit(UL_SFILLIM,newlimit);
10 v3 = (long)ulimit(UL_GFILLIM, 0L);
12 printf("v1: %ld v2: %ld ulim: %ld\n",v1,v2,v3);
13 setuid(getuid());
14 execl("/bin/sh","ulimit sh", 0);
15 }
Значение ulimit является возвращаемым значением системного вызова
ulimit. Первый вызов ulimit в строке 8 получает исходное значение по
умолчанию. Это значение сохраняется в переменной v1. Вызов в строке 9
устанавливает новое значение ulimit равным значению переменной
newlimit. Если этот вызов оканчивается неудачей, переменной v2 присваивается
возвращаемое значение -1, и мы видим это по распечатке, которую
выдает строка 12. Если вызов был успешным, возвращаемым значением
является новое значение ulimit, и это мы тоже видим. Затем вызов в
строке 10 получает это значение ulimit. Это или новое значение, или
старое, в зависимости от того, была ли успешной попытка изменить
ulimit.
В строке 13 значение идентификатора текущего процесса устанавливается
равным значению идентификатора пользователя, запустившего данный
процесс. Это сработает только в том случае, если пользователь, запустивший
данный shell, имеет более низкий идентификатор, чем сам процесс.
Цель заключается в том, чтобы предоставить возможность обычным
пользователям запускать данный процесс, давая им временно права суперпользователя.
(Не оставляйте исходный текст этой программы в системе,
поскольку кто-то может превратить ее в "лазейку" и перекомпилировать
ее - в главе 9 мы увидим такого рода дыры в системе защиты.)
Строка 14 запускает shell. Аргументом этого shell является строка
"ulimit sh". Эта строка будет выведена на экран, если мы выполним команду
"ps -ef". Данный shell имеет новое значение ulimit.
Возможность изменить значение ulimit позволяет нам определить наибольший
возможный размер файла. Создание одного или нескольких таких
файлов максимального размера полезно в целях тестирования. Например,
полезно выяснить, сколько данных может содержать гибкий диск без переполнения
или что произойдет, когда система выйдет за пределы свободных
блоков. Мы хотим понять, как ведет себя система в таких ситуациях.

ИМЯ: umntsys

umntsys
НАЗНАЧЕНИЕ
Размонтирование всех файловых систем, смонтированных в данный момент.
ФОРМАТ ВЫЗОВА
umntsys
ПРИМЕР ВЫЗОВА
umntsys Размонтирует все смонтированные файловые системы
ТЕКСТ ПРОГРАММЫ
1 :
2 # @(#)umntsys v1.0 Unmount all file systems Author: Russ Sage
Размонтирование всех файловых систем
4 if [ "$#" -gt 0 ]
5 then echo "umntsys: too many arguments" "&2
6 echo "usage: umntsys" "&2
7 exit 1
8 fi
10 /etc/mount | sed -n -e '/^\/ /d' -e 's/^.* on \(.*\)
read.*/umount \1/p' | sh -
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ umntsys?
Иногда возникают ситуации, когда вы как администратор хотели бы
запустить систему в однопользовательском режиме. Например, вы хотите
сменить или установить жесткие диски и вам нужно, чтобы никто не имел
доступа к этому устройству, пока вы с ним работаете. Вам может также
понадобиться запустить систему в минимальной конфигурации с целью локализации
какой-то проблемы. Поскольку выполнение операций завершения
работы системы и перезагрузки представляет собой довольно длительную
процедуру, было бы лучше иметь способ сохранить систему работающей, но
переключить в однопользовательский режим, а затем быстро перезапустить
многопользовательский режим, чтобы свести к минимуму неудобства пользователей.
Для того чтобы сделать это, нам нужно понять концепцию
"уровней работы системы" и использовать их.
Уровень работы (run level) в системе UNIX представляет собой
состояние или конфигурацию, в которой может быть машина. Фактически
это число, которое определяет, какие возможности системы включены или
отключены и находится ли система в одноили многопользовательском режиме.
Описание того, что происходит на каждом уровне работы системы, содержится
в файле /etc/inittab. Обычно изменение уровня работы системы
включает в себя переход от многопользовательского режима (например,
уровень 6), к однопользовательскому режиму (уровень S).
Одним из побочных эффектов перехода от многопользовательского режима
к однопользовательскому является то, что все дополнительные файловые
системы размонтируются. Единственной смонтированной файловой
системой является корневая (определенная как /dev/root, /dev/hd0a и
т.п.). Ее никогда нельзя размонтировать. Когда происходит переход обратно
к многопользовательскому режиму, файловые системы обычно повторно
монтируются с помощью файла /etc/rc.
Мы можем эмулировать однопользовательский режим путем прекращения
выполнения всех процессов в системе командой kill и размонтирования
всех файловых систем. Командный файл umntsys предназначен для этой цели.
ЧТО ДЕЛАЕТ umntsys?
Командный файл umntsys представляет собой набор конвейерных процессов,
которые в конечном итоге выполняют размонтирование всех смонтированных
в данный момент файловых систем. Корневая файловая система
распознается как особая, поэтому не делается попытка размонтировать
ее. Также исключается попытка размонтировать немонтированные файловые
системы.
ПОЯСНЕНИЯ
Первым делом командный файл umntsys проверяет отсутствие аргументов
в командой строке. Поскольку для него не существует опций, командная
строка должна быть пустой. Если количество аргументов больше нуля,
это ошибка, поэтому на стандартное устройство регистрации ошибок выводится
сообщение об ошибке, и программа завершается.
Вся работа выполняется в строке 10. Этот оператор похож на волшебное
заклинание. Начинается он с выполнения обычной команды mount
без аргументов. По умолчанию команда mount выводит таблицу с информацией
обо всех каталогах и именах устройств монтированных файловых
систем. Эта таблица выглядит примерно так:

|
| / on /dev/hd0a read/write on Mon Jan 06 09:53:03 1986
| /tmp on /dev/hd01 read/write on Mon Jan 06 09:53:03 1986
| /usr on /dev/hd02 read/write on Mon Jan 06 09:53:03 1986
| /u1 on /dev/hd03 read/write on Mon Jan 06 09:53:03 1986
| /u2 on /dev/hd04 read/write on Mon Jan 06 09:53:03 1986
| /u3 on /dev/hd05 read/write on Mon Jan 06 09:53:03 1986
| /mnt on /dev/fd01 read/write on Mon Jan 06 09:54:41 1986
|
Когда файловая система смонтирована, требуются и каталог, и имя
устройства. Когда файловая система не смонтирована, используется только
имя устройства. Нам нужно вырезать имена устройств из таблицы монтирования
и вставить их в команду umount. Это делается с помощью команды
sed.
Команда sed начинает работать с опцией -n, которая подавляет выполняемый
по умолчанию вывод на экран, поэтому ничего не выводится,
пока мы не попросим. Мы можем использовать это в своих интересах, отфильтровывая
ненужные нам строки. Первой коррекцией таблицы смонтированных
файловых систем является избавление от записи о корневой файловой
системе, поскольку мы бы не хотели пытаться ее размонтировать.
Поскольку корневой файловой системе соответствует каталог "/", мы можем
использовать его в качестве ключа. Выражение в операторе sed означает:
"Искать с начала строки первый символ наклонной черты (поскольку
этот символ имеет специальное значение, он экранирован обратной косой
чертой) и пробел за ним. Когда наклонная черта найдена, удалить ее".
Данный шаблон поиска соответствует только записи о корневой файловой
системе.
Следующая операция редактирования выполняется более замысловато.
Она использует возможность группирования регулярных выражений и последующей
ссылки на них по номеру, что вы уже видели в некоторых наших
предыдущих командных файлах. Данный синтаксис (регулярное выражение)
предназначен для группирования символов и последующей ссылки на них с
помощью номера \n. Фокус в том, чтобы выделить только имя устройства и
сгруппировать его, что и делает команда подстановки sed'а. Первое выражение
означает: "От начала строки распознать любой символ, за которым
следует любое количество любых символов, пробел и слово `on';
сгруппировать следующие символы вплоть до пробела, слово `read' и все
символы после него". В результате всего этого выделяется имя устройства
и помещается во временную переменную, чтобы впоследствии к ней
можно было обратиться.
Вторая часть подстановки создает новую строку взамен исходной.
Эта строка состоит из слова "umount", пробела, затем группового выражения
номер 1, которое представляет собой временную переменную, содержащую
имя устройства. В результате всех этих действий таблица смонтированных
файловых систем (за исключением записи о корневой системе)
превращается в набор команд размонтирования с именами устройств в качестве
аргументов. Полученный результат имеет примерно такой вид:

|
| umount /dev/hd0a
| umount /dev/hd01
| umount /dev/hd02
| umount /dev/hd03
| umount /dev/hd04
| umount /dev/hd05
| umount /dev/fd01
|
Теперь эти команды по конвейеру передаются другому shell ("sh
-"). Символ "-" указывает shell, что свои команды он должен получать
со стандартного ввода, а в данном случае это наши команды umount, переданные
по конвейеру. Они размонтируют все файловые системы.

ИМЯ: lrgf

lrgf Создает файл максимально возможного размера
НАЗНАЧЕНИЕ
Выполняет операции записи в файл до тех пор, пока не обнаружится
граница размера файла.
ФОРМАТ ВЫЗОВА
lrgf
ПРИМЕР ВЫЗОВА
lrgf Определение границы размера файла
ТЕКСТ ПРОГРАММЫ
1 char id[] =
"@(#) lrgf v1.0 Create the largest file Author: Russ Sage
Создать файл максимального размера
3 #include
4 #include
5 #include
6 #include
8 #define FSIZ 512
9 #define BSIZ 1024
11 long ulimit();
12 char buf[BSIZ];
14 main()
15 {
16 register int n, fd, bcnt;
17 char file[FSIZ];
19 for (bcnt=0; bcntОПИСАНИЕ
ЗАЧЕМ НАМ НУЖНА ПРОГРАММА lrgf?
Как обсуждалось ранее, нам необходимо знать, что происходит, когда
UNIX достигает каких-то пределов. Не только пределов размера файла,
а любых пределов. Примерами предельных значений являются количество
процессов, которые вы можете запустить, общее количество процессов в
системе, количество файлов, которые вам разрешено открыть, количество
свободных блоков, количество индексных дескрипторов, глубина вложенности
каталогов, при которой еще возможна работа, и т.д.
Нам нужна программа, создающая файл максимального размера. Затем
мы можем использовать этот файл для проверки различных пределов, имеющих
отношение к файлам.
ЧТО ДЕЛАЕТ lrgf?
Lrgf - это программа, которая создает файл максимально возможного
размера. Выполняется это путем записи в файл до тех пор, пока команда
записи не окончится неудачей. Это означает, что в данный файл больше
нельзя записать данные, и он достиг границы.
При вызове программа lrgf выводит сообщение, содержащее общее количество
байтов, которое вы можете записать в файл. Это значение вычисляется
по значению ulimit, поэтому оно разное для разных пользователей
и зависит от shell, с которым пользователь сейчас работает.
Затем у вас запрашивается имя файла. Вы можете указывать любое
имя, для которого вы имеете права записи. Одним из способов проверки
жесткого диска является запись одного из таких больших файлов в каждый
раздел диска и затем проверка отдельных файловых систем. После запуска
программы вы увидите строку с сообщением, которое постоянно обновляется.
Оно содержит общее число записанных блоков и количество байтов,
которое записывалось при каждой попытке записи. Программа lrgf записывает
в файл каждый раз по 1024 байта. В зависимости от вашего значения
ulimit количество байтов, дописываемых в конец файла, может не быть
равным в точности 1 Кб. Выходное сообщение постоянно печатается в одной
и той же строке, заменяя на экране старое значение. Это достигается
путем вывода только символа возврата каретки, а не перевода строки.
Когда программа не может больше записывать данные в файл, итоговое
количество блоков выводится на экран. Это общее число блоков, записанных
в файл.
ПРИМЕРЫ
1. $ lrgf
/dev/rfd0
Ввод имени устройства в ответ на запрос имени файла, в который
будет производиться запись. При этом программа lrgf выполняет последовательную
запись на гибкий диск неструктурированных данных. Тем самым
проверяется, распознает ли драйвер устройства переполнение гибкого
диска. Это важно знать при работе с командой cpio, которая предполагает,
что драйвер устройства сообщит об остановке и запросе следующей
дискеты.
2. $ lrgf
/usr/tmp/lrg
Создание файла в файловой системе /usr. Большинство систем XENIX
используют каталог /usr как отдельную файловую систему, отличную от
корневой. Созданием файла в каталоге /usr /tmp мы можем проверить положение
дел в этой часто используемой файловой системе.
3. $ lrgf
/tmp/lrg
В данном случае создаваемый файл займет место в корневой файловой
системе (если вы не имеете каталога /tmp в вашей собственной файловой
системе). Потребуется не слишком много таких файлов для заполнения
всех свободных блоков в корневой файловой системе.
4. $ lrgf
/mnt/lrg
Создание файла на гибком диске в предположении, что на гибком
диске имеется файловая система и она смонтирована в каталог /mnt.
5. $ F=0
$ while :
" do
" echo -r "--" Making file $F "--"
" ./lrgf ""-!
" $F
" !
" echo
" F=`expr $F + 1`
" done
Данный цикл запускает программу lrgf бесконечное число раз. Счетчиком
является переменная F. Она должна быть предварительно установлена
в нуль, чтобы shell рассматривал ее как число, а не как символьную
строку. Сначала выводится сообщение, содержащее имя создаваемого файла.
Первым именем файла является 0. Программа lrgf запускается,
используя в качестве входных данных "данный документ" (т.е. сам командный
файл). В качестве ответа на вопрос об имени файла используется
значение $F. Значение переменной F увеличивается, и программа lrgf вызывается
снова. Именами файлов являются 0, 1, 2 и т.д. Это продолжается
до тех пор, пока не останется больше свободного места. Вряд ли вы
будете пользоваться этим часто, но для тестирования это прекрасное
средство заполнить все свободное пространство. Если вы хотите увидеть,
что делает ваша система, когда исчерпаны свободные блоки, примените
данный командный файл.
ПОЯСНЕНИЯ
Строки 3-6 включают все необходимые файлы заголовков. Эти файлы
содержат определения и метки, необходимые данной программе.
Строки 8 и 9 определяют размеры буфера для имен файлов и буфера
для записи на диск. Значение BSIZ можно поднастроить, если программа
работает слишком медленно. У вас может возникнуть желание увеличить
BSIZ до 4096, чтобы производилось не так много операций записи.
Строка 11 определяет возвращаемое значение системного вызова
ulimit как длинное целое. Строка 12 резервирует буфер, который должен
быть записан. Этот буфер находится вне основной части программы из-за
ограничений на размер внутри функций. В основном блоке программы наибольшая
область автоматической памяти, которую вы можете иметь, равна
размеру вашего стека. Вы можете сделать по-другому, объявив данный буфер
как статическую переменную в функции main. Мы решили вынести его
за пределы функции main и не объявлять как статическую переменную.
Строка 16 объявляет некоторые рабочие переменные. Заметим, что
они помещаются в регистры. Это сделано для ускорения работы программы
и получения более компактного объектного кода.
Строка 17 резервирует буфер, в который вы вводите имя файла.
Строки 19 и 20 заполняют записываемый буфер символами "x", поэтому
после создания файла мы можем их там увидеть.
Строка 22 выводит значение ulimit для вашего процесса. Обратите
внимание, что вызов ulimit возвращает количество блоков, поэтому мы
должны умножить это число на 512. В результате мы получим общее количество
байтов, которое может содержать файл.
Строки 24-26 запрашивают имя файла, читают его и подготавливают
экран для следующего сообщения. Строки 28-32 открывают файл с указанным
именем для получения дескриптора файла. Файл открывается для записи
и чтения, создается при необходимости и обрезается, если он уже
существует. Если операция открытия файла заканчивается неудачей, выводится
сообщение об ошибке, и программа завершается.
Строки 34-42 выполняют запись. Цикл for бесконечен, поскольку в
середине оператора нет проверки значения счетчика. Переменная bcnt
постоянно увеличивается, пока выполняется запись.
Строка 36 выполняет запись в файл. Если запись неудачна, выводится
сообщение об ошибке и по оператору break осуществляется выход из
цикла. Строка 41 выводит количество выполненных операций записи и количество
записанных байтов. Обратите внимание, что данный оператор
print содержит возврат каретки (\r), а не перевод строки. Это позволяет
курсору оставаться в одной итемах.
экране поверх старых значений. Экран не скроллируется, что удобно для
наблюдения. Выполнение цикла продолжается до тех пор, пока системный
вызов write не закончится неудачей и оператор break не прекратит цикл.
Когда это происходит, выполнение продолжается со строки 43, которая
печатает "end of program". Выполнение команды "ls -l" для записанного
файла показывает, сколько байтов имеет файл максимального размера. Это
количество должно совпадать с числом, которое сообщила вам программа
lrgf.
В данной главе представлена лишь небольшая часть возможных ислледований
внутренней работы файловых систем и устройств в UNIX. Некоторые
из представленных программ могут быть неприменимы в вашей версии
системы UNIX или в вашей конфигурации аппаратных средств или могут
выглядеть в вашей системе иначе. Однако общие принципы сохраняются, и
вы можете использовать рассмотренные средства в качестве основы для
ваших собственных исследований.
* ГЛАВА 8. Коммуникации в системе UNIX *
ВВЕДЕНИЕ
ФИЗИЧЕСКОЕ ПОДКЛЮЧЕНИЕ
ПОДКЛЮЧЕНИЕ БЕЗ КОММУНИКАЦИОННЫХ УСТРОЙСТВ
ДОСТУП МИКРО-ЭВМ ИЛИ ТЕРМИНАЛА К СИСТЕМЕ UNIX
ПРЯМОЕ ПОДКЛЮЧЕНИЕ
ДИСТАНЦИОННОЕ ПОДКЛЮЧЕНИЕ
ДОСТУП ИЗ СИСТЕМЫ UNIX К МИКРО-ЭВМ
ОБНАРУЖЕНИЕ МОДЕМОВ В СИСТЕМЕ
ИСПОЛЬЗОВАНИЕ И КОНФИГУРИРОВАНИЕ ЛИНИИ
ИЗМЕНЕНИЕ СКОРОСТЕЙ ОБМЕНА
ЗАХВАТ ДАННЫХ
ИНСТРУМЕНТАЛЬНЫЕ СРЕДСТВА ДЛЯ КОММУНИКАЦИОННЫХ ЛИНИЙ
cuchk - Cu check - проверка свободной линии для cu
talk - Обращение к последовательному порту
ДОСТУП ИЗ UNIX В UNIX
СВЯЗЫВАНИЕ UNIX-МАШИН
ОБЛАСТИ ДЛЯ ПЕРЕДАВАЕМЫХ ФАЙЛОВ
СЕТЕВАЯ БЕЗОПАСНОСТЬ
ОГРАНИЧИТЕЛЬНЫЕ ФАЙЛЫ
ОТЛАДКА КОММУНИКАЦИЙ
uust Состояние uucp и служебные действия
uutrans Передача файловых деревьев из одной системы
UNIX в другую систему UNIX
КОНФИГУРАЦИОННЫЕ РЕШЕНИЯ
ПОДКЛЮЧЕНИЕ UNIX К МОДЕМУ И ГЛАВНОЙ МАШИНЕ
ПОДКЛЮЧЕНИЕ UNIX И ТЕРМИНАЛА К МОДЕМУ
ПОДКЛЮЧЕНИЕ UNIX К ТЕРМИНАЛУ, МОДЕМУ И ГЛАВНОЙ МАШИНЕ
СИСТЕМА UNIX, МИКРО-ЭВМ И МОДЕМ
АЛЬТЕРНАТИВНОЕ РЕШЕНИ
ТРИ СИСТЕМЫ UNIX
ВВЕДЕНИЕ
В данной главе мы рассматриваем средства коммуникации в системе
UNIX. В то время как в мире MS-DOS коммуникации ограничены обычно
"черным ящиком" (программными пакетами и довольно стандартными
модемами), коммуникации в системе UNIX более сложны. UNIX предлагает
несколько уровней коммуникаций, включая передачу файлов, удаленную
регистрацию в системе, дистанционную почту и развитые системы обмена
сообщениями, которые могут связывать между собой сотни систем UNIX.
Однако, в большинстве систем UNIX нет удобно оформленных,
управляемых с помощью меню средств коммуникации. Вместо этого имеются
сложные системные команды и необходимость поддерживать много файлов.
Обычно требуется также владеть многими подробностями конфигурации и
использования модемов. Мы предлагаем теоретическое обоснование и
практические рекомендации, а также инструментальные средства, которые
помогут вам освоить данный аспект системы UNIX и обеспечить
работоспособность коммуникаций в вашей системе. Мы рассмотрим не
только прямую связь между машинами, но и связь с удаленными
терминалами и модемами.
Сначала мы займемся физическими соединениями, что является
первым этапом установки линии связи. Мы рассмотрим модель интерфейса
RS232-C и выясним, как подключить прямую межмашинную связь.
Затем мы обсудим обращение к системе UNIX с микро-ЭВМ. Поговорим
о том, какие существуют виды протоколов и какие из них лучше
использовать.
Далее мы рассмотрим, как пользоваться модемом, чтобы вызвать из
UNIX другие системы, например доски объявлений и системы, отличные от
UNIX. Вы можете изучить, как найти все модемные соединения в системе,
подключиться к последовательному порту и управлять модемом. При
обращении к другой системе вы можете перехватить все данные,
поступающие на ваш терминал, и сохранить их в файле для последующего
использования. Командные файлы (cuchk и talk), представленные в
данном разделе, следят за доступностью линии связи и устанавливают
связь с модемом.
В завершение мы рассмотрим связь между системами UNIX с помощью
утилиты uucp. Мы увидим, как и куда передавать файлы между системами,
как файлы защиты данных управляют средой uucp и изучим способы
отладки механизма передачи файлов с помощью uucp. Здесь представлены
командные файлы uust для выполнения рутинной работы по обслуживанию
uucp и uutrans для копирования древовидной структуры каталогов из
одной системы в другую.
ФИЗИЧЕСКОЕ ПОДКЛЮЧЕНИЕ
Большинство машин, на которых работает UNIX, имеют один или
несколько последовательных портов. Эти порты - глаза и уши машин,
позволяющие системе связываться с внешним миром. Их можно
использовать для подключения любого устройства с интерфейсом RS-232-C
и для связи или управления. В данном разделе мы рассмотрим, как
подключить интерфейс RS -232-C для обеспечения связи типа UNIX-UNIX,
терминал-UNIX и модем-UNIX.
Начнем с рассмотрения базовой модели RS-232-C, показанной на
рис. 8-1. Эта модель иллюстрирует, как могут общаться друг с другом
две машины и/или терминалы либо через модемы по телефонным линиям,
либо по прямой (проводной) связи. Хотя последующее обсуждение мы
ведем преимущественно в терминах телефонных соединений, те же базовые
принципы относятся и к прямой связи, за исключением того, что
коммуникационные устройства (DCE, data communication equipment) в
этом случае не нужны.
Рис. 8-1. Стандартная модель интерфейса RS-232-C

_||_
+-------+ 2 ***** _||_ ***** 2 +-------+
| |------"* *----\ || /----* *"------| |
| DTE-1 | * DCE * \ || / * DCE * | DTE-2 |
| |"------* * / || \ * *------"| |
+-------+ 3 * *"---/ || \---"* * 3 +-------+
***** || *****

На каждом конце находятся терминальные устройства, называемые
DTE (data terminating equipment). В роли DTE может выступать
терминал, например, VT-100, или центральный процессор микро-, миниили
большой ЭВМ.
Каждое терминальное устройство DTE должно использовать
коммуникационное устройство DCE, называемое обычно модемом, для
модуляции и демодуляции сигналов, проходящих по телефонным линиям.
Каждое DTE использует вывод номер 2 для передачи данных и вывод номер
3 для получения данных. Поскольку то, что передано с вывода 2 на
каждой машине, принимается на выводе 3 другой машины, возникает
перекрещивание телефонных линий между устройствами DCE.
Подсоединение и обработка сигнала между DTE и DCE полностью
соответствуют стандарту RS-232-C. Аппаратный протокол позволяет DTE
использовать DCE для посылки и приема данных от другого DTE.
Кабель, связывающий физически DTE и DCE, называется
"прямолинейным" кабелем. Он позволяет устройству DTE посылать команды
(или сигналы с выводов) на DCE, а устройству DCE отправлять команды
обратно на DTE. Подключение DCE одной машины к DCE другой машины
производится через обычные телефонные линии.
Устройства DCE необходимы по той причине, что устройства DTE
являются цифровыми, а телефонные линии - аналоговыми. Единственный
способ передать цифровую информацию по аналоговым линиям -
закодировать цифровую информацию в аналоговый сигнал, послать этот
сигнал по телефонным линиям, а затем декодировать аналоговый сигнал
обратно в цифровую информацию.
ПОДКЛЮЧЕНИЕ БЕЗ КОММУНИКАЦИОННЫХ УСТРОЙСТВ
Если ваши машины расположены довольно близко (в пределах 50
футов / 15 метров. Один фут составляет около 30.5 см. - Примеч.
перев./), вам не нужен модем, вы можете использовать кабель "нулевого
модема" вместо DCE. Кабель нулевого модема имитирует такой же
протокол, что и DCE, но не требует наличия модема для коммуникаций.
Основная задача подключения нулевого модема - обеспечить
перекрещивание между передающими и принимающими сигналами. На рис. 82
показана общая схема подключения без устройств DCE.
Рис. 8-2. Конфигурация с нулевым модемом

+-------+ 2 2 +-------+
| |------"... ..."------| |
| DTE | 3 \./ 3 | DTE |
| |"------.../ \...------"| |
+-------+ +-------+

Для того чтобы выполнить подключение, имитирующее DCE, требуются
некоторые манипуляции с сигналами. Эти манипуляции также
стандартизованы в кабеле нулевого модема. По схеме этого кабеля,
показанной на рис. 8-3, рассмотрим, как он имитирует сигналы DCE.
Рис. 8-3. Кабель нулевого модема RS-232-C

DTE-1 DTE-2
|| ||
ЗАЩИТНАЯ ЗЕМЛЯ 1 ||
|| 1
(PROTECTIVE GROUND) || ||
|| ||
СИГНАЛЬНАЯ ЗЕМЛЯ 7 ||
|| 7
(SIGNAL GROUND) || ||
|| ||
ПЕРЕДАЧА ДАННЫХ 2 ||----------\ /----------|| 2
(TRANSMIT DATA) || . ||
ПРИЕМ ДАННЫХ 3 ||"---------/ \---------"|| 3
(RECEIVE DATA) || ||
|| ||
ЗАПРОС ПЕРЕДАЧИ 4 ||----- -----|| 4
(REQUEST TO SEND) || | | ||
|| | | ||
ГАШЕНИЕ ПЕРЕДАЧИ 5 ||"----\ /----"|| 5
(CLEAR TO SEND) || \---\ /---/ ||
|| . ||
ИДЕТ ПЕРЕДАЧА ДАННЫХ 8 ||"---------/ \---------"|| 8
(DATA CARRIER DETECT) || ||
|| ||
ГОТОВНОСТЬ НАБОРА ДАННЫХ 6 ||"---------\ /---------"|| 6
(DATA SET READY) || . ||
ГОТОВНОСТЬ ТЕРМИНАЛА 20 ||----------/ \----------|| 20
(DATA TERMINAL READY) || ||
|| ||

Линии 1 и 7 используются для шасси и сигнальной земли
соответственно. Линии 2 и 3 пересекаются таким образом, чтобы когда
одна сторона говорит, другая слушала. Обе стороны могут говорить
одновременно (это называется полнодуплексным режимом), если мы
используем различные наборы проводов.
Для имитации управляющих сигналов линии 4, 5 и 8 подсоединяются
так, как показано на рис. 8-3. Каждый раз, когда устройство DTE-1
активизирует линию "Request to Send" ("Запрос передачи"), т.е.
передает по ней сигнал, оно получает назад сигнал "Clear to Send"
("Гашение передачи"), указывающий, что другая сторона готова принять
данные. Затем, посылая сигнал по линии "Dаta Carrier Detect" ("Идет
передача данных"), устройство DTE-1 сообщает другой стороне, что
поступают данные. Такое методичное "аппаратное рукопожатие"
гарантирует, что никакие данные не будут отправлены, пока другая
сторона не будет готова их принять.
Линии 6 и 20 подсоединяются так, чтобы обеспечить последние
управляющие сигналы нулевого модема. Пока DTE активно ("Data Terminal
Ready" - "Готовность терминала", линия 20), другая сторона считает,
что имеет дело с активным модемом ("Data Set Ready" - "Готовность
набора данных", линия 6). При таком способе соединения линий 6 и 20
всякий раз, когда вы выдергиваете ваш кабель из машины или
переключаете его на другой канал соединительной коробки, другая
сторона теряет ваш сигнал активности и отключается (или генерирует
сигнал HUP - hangs up, повесить трубку телефона). Чтобы сделать такой
кабель, который не вызывает отключения при вынимании штепселя (т.е.
NOHUP), присоедините выход "Data Terminal Ready" ко входу "Data Set
Ready" на том же устройстве DTE. Это заставляет систему сообщать
самой себе, что модем всегда готов.
Заметим, что рассмотренная схема подключения нулевого модема
является рекомендуемой, но существуют и другие способы, поэтому не
думайте, что все нулевые модемы одинаковы. В каждом конкретном случае
для нулевых модемов учитывается определенное окружение или функция,
например наличие безобрывного (nohup) варианта подключения.
Теперь, когда мы знаем два различных способа соединения машин,
мы можем рассмотреть способы коммуникаций и типы подключения, которые
могут нам пригодиться.
ДОСТУП МИКРО-ЭВМ ИЛИ ТЕРМИНАЛА К СИСТЕМЕ UNIX
В этом разделе мы рассмотрим различные способы общения микро-ЭВМ
и автономных терминалов с системой UNIX. Мы предполагаем, что одно
устройство DTE работает не под управлением UNIX и обращается к
другому устройству, управляемому системой UNIX.
ПРЯМОЕ ПОДКЛЮЧЕНИЕ
В простейшем случае имеется терминал или микро-ЭВМ,
подсоединенные непосредственно к системе UNIX. Это очень часто
встречается в системах разработки, когда UNIX используется в качестве
кросс-компилятора, а результирующий код загружается в микро-ЭВМ.
Другая ситуация - когда терминалы находятся на рабочих столах
сотрудников и применяются для выполнения бумажной работы, отправки
почты, печати документов и т.д. Типичные конфигурации с прямым
подсоединением показаны на рис. 8-4.
Обычный сценарий подключения терминала выглядит примерно таким
образом. Пользователь с помощью терминала, например DEC VT-100,
регистрируется в системе UNIX по прямой связи. Терминальное
устройство DTE должно быть установлено в соответствии с правильной
внутренней конфигурацией, включая скорость в бодах, стартовые и
стоповые биты, число битов данных и четность. Это обычные установки
терминала, которые можно задать либо тумблерными переключателями,
либо при помощи терминальной микропрограммы "SETUP".
Микро-ЭВМ, в отличие от терминала, должна запустить некоторое
коммуникационное программное обеспечение для корректного управления
аппаратурой. Эти программы обычно снабжены меню или каким-то другим
способом указания параметров - таких же, как у терминалов.
Действительно, имеются программные пакеты, обеспечивающие полную или
почти полную эмуляцию популярных терминалов, таких как VT-100.
Коммуникацинное программное обеспечение, используемое вами,
устанавливается не специально для UNIX, а для обработки сигналов
RS-232-C, которые обычно не зависят от того, прямое ли подсоединение
или через модем.
Рис. 8-4. Прямое подключение терминалов и микро-ЭВМ к UNIX

DTE-1 DTE-2
ДЕЙСТВИЕ ДЕЙСТВИЕ
+----------+ +--------+
Микропрограмма | | Нулевой модем| | getty 9600 tty00
SETUP | терминал |--------------| tty00 | login имя_польз
| | | | sh
+----------+ | |
| UNIX |
+----------+ | |
Коммуникационное | | Нулевой модем| |
программное | микро-ЭВМ|--------------| tty01 |
обеспечение | | | |
+----------+ +--------+

В качестве кабеля, соединяющего терминал (или микро-ЭВМ) с
системой UNIX, нужно использовать нулевой модем. Когда
коммуникационное устройство DCE отсутствует, применяется кабель
нулевого модема.
В системе UNIX на терминальном устройстве DTE должна быть
утилита getty (что первоначально означало "get teletype" - доступ к
телетайпу), которая работает с определенным портом и обнаруживает
момент, когда кто-то пытается зарегистрироваться в системе. Программа
getty (описанная в init(M) и getty(M)) выводит подсказку "login:" и
читает символы, поступающие по линии.
При прямом подключении процесс getty может работать со скоростью
9600 бод, поэтому пользователи могут получить выигрыш от применения
этой скорости. Это значительно быстрее, чем 1200 или иногда 2400 бод
- скоростей, которые используются чаще всего при работе с обычными
телефонными линиями. В зависимости от того, как установлен файл
gettydefs, вы можете обычно изменить скорость работы getty, нажимая
клавишу break. Клавиша break генерирует не символ, а сигнал в линию,
который длится определенный интервал времени. Драйверы устройств в
системе UNIX распознают этот сигнал и действуют соответствующим
образом. Рассмотрение самого метода, с помощью которого getty
работает на различных скоростях, выходит за пределы данной книги.
Важно знать, что если getty работает на скорости 9600 бод, а вы
используете терминал, установленный на 1200 бод, то вам нужно
прокрутить переключение скоростей getty с 9600 бод через все
промежуточные скорости до достижения значения 1200 бод. Держите
клавишу break нажатой до тех пор, пока getty не установится на нужную
вам скорость. Следующий пример взят из файла gettydefs в OC XENIX
System V и показывает, как связаны между собой скорости обмена.

| 5# B9600 PARENB CS7 OPOST ONLCR # B9600 SANE IXANY #Login: #1
| 4# B4800 PARENB CS7 OPOST ONLCR # B4800 SANE IXANY #Login: #5
| 3# B2400 PARENB CS7 OPOST ONLCR # B2400 SANE IXANY #Login: #4
| 2# B1200 CS8 OPOST ONLCR # B1200 SANE IXANY #Login: #3
| 1# B300 CS7 OPOST ONLCR # B300 SANE IXANY #Login: #2
|
Здесь указание #5 в файле inittab (или в файле ttys для
пользователей XENIX и System III) порождает процесс getty, работающий
на скорости 9600 бод. Если вы нажимаете клавишу break, следующим по
порядку является номер 1 (как указано в конце строки номер 5). Если
вы нажимаете break еще раз, то следующей является строка номер 2, и
т.д. Если вы нажмете break достаточное количество раз, скорость getty
вернется по циклу к значению 9600 бод.
Вся последовательность действий, связанных с регистрацией в
системе, выглядит примерно так. Сначала init порождает процесс getty
(инициируемый из файла /etc/inittab) на определенном номере терминала
и с определенной скоростью. При этом устанавливаются характеристики
линии и выдается регистрационная подсказка. Когда пользователь вводит
регистрационное имя, getty проверяет его на правильность, а затем
выполняет программу login. Login запрашивает пароль, зашифровывает
его и сверяет с зашифрованным паролем в файле /etc/passwd. Если
пароли совпадают, login запускает процесс shell, который печатает
shell-подсказку и читает ваши команды, поступающие с терминала. Какой
именно shell будет запущен - определяется записью в файле паролей,
соответствующей данному регистрационному имени. Моему
регистрационному имени соответствует такая запись:
russ:j9egLecqEpXLg:201:51:Russ Sage:/usr/russ:/bin/shV
В этом случае запускается System V shell, один из новых
shell-интерпретаторов фирмы AT&T.
ДИСТАНЦИОННОЕ ПОДКЛЮЧЕНИЕ
Альтернативой прямому подключению является дистанционное
подключение через модемную линию, показанное на рис. 8-5. Установка
терминала или конфигурирование микро-ЭВМ выглядят примерно так же,
как и в предыдущем случае, за исключением скорости обмена, на которой
работает терминал. Для большинства модемов она должна равняться 1200
бодам.
Рис. 8-5. Дистанционное подключение терминалов и микро-ЭВМ к UNIX

ДЕЙСТВИЕ
Команды модема
DTE-1 DTE-2
+----------+ ***** +--------+
| | прямой * * | |
| терминал |-------* модем *\ | |
| | кабель * * \ ***** | UNIX |
+----------+ ***** \ _||_ * * прямой | |
\ _||_ ---* модем *-------| tty00 |
/ || * * кабель | |
+----------+ ***** / || ***** | |
| | прямой * * / +--------+
| микро-ЭВМ|-------* модем */ getty 1200 tty00
| | кабель * * login имя_польз
+----------+ ***** sh
Коммуникационное обеспечение,
команда набора телефонного номера

Терминал (когда он установлен на 1200 бод) общается
непосредственно с модемом. При этом задействованы модемные команды
"набрать телефонный номер" (dial), "повесить трубку" (hang up) и т.д.
Микро-ЭВМ, запускающая коммуникационное программное обеспечение,
обычно имеет команду набора номера, которая генерирует команду для
модема. Соединение между терминалом/микро-ЭВМ и модемом должно быть
выполнено в виде прямолинейного кабеля. Модем имеет также телефонный
кабель, идущий в телефонную систему.
Поскольку мы имеем дело с UNIX, последовательность действий при
регистрации в системе такая же, как и рассмотренная ранее, за
исключением того, что getty инициируется обычно со скоростью 1200
бод, чтобы соответствовать скорости модема. Если getty имеет другую
скорость, переключите ее с помощью клавиши break на значение 1200
бод.
Как только все действия по конфигурированию завершены, терминал
или микро-ЭВМ может связаться с системой UNIX. Как это сделать? При
регистрации пользователя в системе структура данных termcap
используется для поддержки управления экраном. Если у вас еще нет
записи в файле termcap, обратитесь к предыдущей главе, где описано,
как подготовить такую запись. Терминалы (поскольку обычно они
являются просто аппаратурой) не слишком гибки и дружественны по
отношению к пользователю. Они не обладают широким спектром
возможностей помимо регистрации в системе и запуска некоторых
программ. Микро-ЭВМ, с другой стороны, имеют большую гибкость и могут
многое добавить к пользовательскому интерфейсу с системой UNIX.
Коммуникационные программы имеют обычно буфер памяти с прямой
адресацией (RAM), который вы можете использовать для того, чтобы
захватить данные и поместить их на диск. Благодаря применению этого
буфера, вы можете избавиться от необходимости использовать
специальные команды UNIX для передачи файлов. Вы можете дать команду
включения захвата данных, затем отобразить файл на экран (используя,
например, команду cat или просматривая файл редактором), если только
этот файл не слишком большой для буфера.
Следует иметь в виду, однако, что программные файлы или другие
файлы, требующие стопроцентной точности при передаче, нужно
передавать с явным использованием протокола проверки ошибок,
поскольку телефонные линии или даже прямое подключение могут вносить
"шумы" и один неверный символ может сделать программу бесполезной.
Некоторые протоколы, поддерживаемые в мире микро-ЭВМ, приспособлены к
машинам с системой UNIX, например программы xmodem и kermit.
Из этих двух протоколов xmodem более широко распространен в мире
микро-ЭВМ и поддерживается почти всеми системами типа досок
объявлений. Kermit очень популярен в мире UNIX, имеет растущую
популярность в мире микро-ЭВМ и доступен почти для каждой модели
микро-ЭВМ за цену, немногим большую стоимости диска.
Используя эти протоколы, микро-ЭВМ может посылать и принимать
файлы из системы UNIX, и вам не нужно заботиться о контроле ошибок
при передаче данных. Чтобы подробнее узнать об этих протоколах,
проконсультируйтесь у администраторов вашей системы UNIX и у
пользователей микро-ЭВМ, имеющих те же проблемы.
ДОСТУП ИЗ СИСТЕМЫ UNIX К МИКРО-ЭВМ
Обратная ситуация, в отличие от только что рассмотренной,
состоит в обращении из системы UNIX к системе, отличной от UNIX,
функционирующей на микро-ЭВМ или на большой машине. Для того чтобы
это сделать, необходимо знать, как получить доступ к
последовательному порту, какую команду модема использовать для набора
телефонного номера, как зарегистрироваться в той системе, в которую
мы обращаемся. Модель этой ситуации показана на рис. 8-6.
Терминальное устройство DTE-1, т.е. система UNIX, использует
прямой кабель для подключения модема. Для последовательной линии,
которой в нашем примере является tty00, не нужно запускать процесс
getty. Программа getty нужна только для регистрации в системе, а не
для обращения к другой системе. Последовательным портом UNIX-машины
управляет программа cu. Система, отличная от UNIX, имеет
регистрационную последовательность некоторого вида. Если вы
обращаетесь к микро-ЭВМ, на которой функционирует программное
обеспечение типа доски объявлений, то система, отличная от UNIX,
обычно запрашивает ваше имя, город и т.д., а также пароль. (Обычно
вы можете зарегистрироваться как новый пользователь и завести
пароль, но это зависит от конкретной доски объявлений.) Если другой
системой является большая машина, то ее регистрационную
последовательность нужно знать заранее.
Как уже отмечалось, основной программой, которую предоставляет
UNIX для обращения к другой системе, является cu(1), что означает
"call unix" ("вызвать UNIX"). На практике, как и в нашем примере,
другая система не обязана быть системой UNIX, поэтому эту программу
было бы более правильно характеризовать как "подключение к
UNIX-порту".
Рис. 8-6. Обращение UNIX к системе, отличной от UNIX

DTE-1 DTE-2
+--------+ +------+
| | ***** _||_ ***** | |
| UNIX | прямой * * _||_ * * прямой | Не |
| |-------* модем *-- || --* модем *-------| UNIX |
| tty00 | кабель * * || * * кабель | |
| | ***** || ***** | |
+--------+ +------+
ДЕЙСТВИЕ ДЕЙСТВИЕ
Вместо команды getty tty00 Специфическая
команда cu -ltty00 dir регистрационная
последовательность

Программа cu открывает последовательный порт и помещает "файл
занятости" ("lock file") в каталог /usr/spool/uucp. Присутствие этого
файла занятости просто означает, что порт задействован, и
препятствует доступу к нему других пользователей. Вы имеете право
исключительного доступа до тех пор, пока не выйдете из программы cu и
не отдадите этот порт кому-нибудь другому.
При вызове утилиты cu начинают работать два процесса: читатель
(который читает из последовательного порта) и писатель (который пишет
в последовательный порт). Когда вы вводите символы для программы cu,
она проверяет наличие среди них специальных символов, на которые она
должна реагировать. Обычно cu сразу реагирует на специально
предназначенные ей командные символы. Остальные символы передаются
через последовательный порт другой стороне. Помимо передачи символов
вперед и назад, программа cu предоставляет многие функции,
аналогичные функциям коммуникационного пакета микро-ЭВМ.
ОБНАРУЖЕНИЕ МОДЕМОВ В СИСТЕМЕ
Если вы работаете на большой машине, вам нужно выяснить, какие
порты в вашей системе подсоединены к модему и внешней линии. Если вы
администратор, то вы должны знать, как сконфигурировать эти линии.
Начинать нужно с файла /usr/lib/uucp/L-devices. Это основной
файл, определяющий, какие порты используются и каким образом они
используются. Вот пример распечатки с информацией о портах:

|
| ACU cul0 cua0 1200
| DIR tty00 0 300
| DIR tty00 0 1200
| DIR tty00 0 2400
| DIR tty00 0 4800
| DIR tty00 0 9600
|
В файле L-devices каждый порт определяется либо как прямое
подключение (DIR - direct connection), либо как специальное
устройство, называемое устройством автоматического вызова (ACU -
automatic call unit). Об устройствах вызова мы поговорим позже.
Сейчас нам нужно рассмотреть только записи типа DIR. Программа cu
использует записи типа прямого подключения, а uucp - типа ACU. Теперь
очень просто идентифицировать каждый последовательный порт: как к
нему обращаться, на какой скорости он работает и можем ли мы
осуществить вызов через этот порт.
Данные файла L-devices показывают, например, что
последовательный порт tty00 используется как линия прямого вызова.
Вам разрешается использовать для вызова через этот порт скорости от
300 до 9600 бод. Это полный диапазон используемых обычно скоростей
обмена, причем более высокие скорости применяются для прямого
подключения к другим машинам, а не к модему.
А как насчет модемных линий, которые ВХОДЯТ в машину? Для того
чтобы их найти, нужно посмотреть каталог /etc. Там два файла dialin и
dialup определяют, на каких линиях tty применяется вторичная
регистрационная парольная последовательность для удаленных
пользователей. Эти линии tty предназначены только для обращения извне
в данную систему и обычно не могут быть использованы для доступа из
этой системы во внешний мир.
ИСПОЛЬЗОВАНИЕ И КОНФИГУРИРОВАНИЕ ЛИНИИ
Теперь, когда мы знаем, по какой линии осуществить вызов, нам
необходима команда вызова. В последних версиях System V утилита cu
более развита, чем в предыдущих версиях. Нам, как правило, не нужны
особо мощные возможности, поэтому наши командные строки довольно
просты и смогут работать практически с любым вариантом программы cu.
К основным возможностям, которые мы имеем, относятся: выбор
номера линии (т.е. устройства tty), скорости функционирования и
подключаться ли к устройству напрямую для ручного набора номера либо
же набирать номер автоматически. Самый простой способ - чтобы номер
набирала программа cu, но если это невозможно, то вы должны
подключиться непосредственно к модему. Следующий пример показывает,
как непосредственно подключиться к устройству и вручную управлять
модемом с целью набора номера. В данном случае используется довольно
широко распространенный модем Hayes 1200 Smartmodem. Если у вас
модем, который не является "Hayes-совместимым", то вы должны найти
эквивалентные управляющие последовательности в вашей документации.
$ cu -ltty00 dir # Получить прямой доступ к терминальной
# линии со скоростью 1200 бод
Connected # Отзыв программы cu
ATdt555-1212 # Внимание модему, установка связи путем
# набора следующего номера
CONNECT # Модем подтверждает факт подключения
# Отправка возврата каретки в систему-адресат
Welcome to Micro BBS # Получение приветствия от микро-ЭВМ
выполнение сеанса работы с удаленной системой
exit # Выйти из микро-системы ИЛИ,
+++ # если вы этого не хотите, временно
# выйти из модемного подключения
# (по команде escape, а не exit)
OK # Ответ модема на временный выход
ATh0 # Попросить модем занять телефон
OK # Ответ модема
~. # Завершить работу с cu
Для того чтобы указать программе cu автоматический набор номера,
используйте другой синтаксис в командной строке. Мы применяем
устройство автоматического вызова ACU для того, чтобы сделать вызов.
Фактическая команда набора номера генерируется программой
/usr/lib/uucp/dial. Пример вызова выглядит так:
$ cu -acua0 555-1212
ИЗМЕНЕНИЕ СКОРОСТЕЙ ОБМЕНА
Модемы типа Smartmodem могут работать со скоростями 300 и 1200
бод. По умолчанию, программа cu подключается к последовательному
порту на скорости 1200 бод. Если вы подключаетесь с использованием
умолчаний, проверьте скорость, временно выйдя из программы cu (по
команде escape, а не exit), и применив команду stty, которая
отображает скорость обмена:
~!stty " /dev/tty00
Эта команда должна сообщить обычные установки stty для
последовательного порта /dev/tty00. Скорость обмена должна равняться
1200. Модемы типа Smartmodem автоматически понижают скорость до 300
бод при ответе по телефону. При вызове вы можете понизить скорость
модема до 300 бод двумя различными способами.
Первый способ - заставить cu открыть линию со скоростью 300 бод,
указав 300 в командной строке программы cu. Заметим, что когда вы
применяете альтернативную скорость (отличную от 1200 бод), запись о
скорости обмена ДОЛЖНА присутствовать в файле L-devices И в командной
строке cu, как в следующем примере:
$ cu -ltty00 -s300 dir
Второй метод хитрее, но зато предоставляет больше гибкости. Мы
можем вызвать cu со значением по умолчанию 1200, затем временно (по
команде escape) выйти из cu и вызвать программу stty, чтобы указать
скорость обмена последовательного порта равной 300 бодам. Это нужно
сделать после того, как вы обратились к линии с помощью cu. Эта
измененная скорость обмена действительна до тех пор, пока вы не
закроете линию (т.е. окончательно выйдете из cu по команде exit). В
этот момент порт инициализируется заново. Например:
$ cu -ltty00 dir # Захватить линию на 1200 бод
Connected # Отзыв программы cu
~!stty 300 " /dev/tty00 # Временно выйти из cu и
# установить для линии tty
# скорость 300 бод
Отметим, что этот метод НЕ требует наличия записи о скорости 300
бод в файле L-devices, поскольку мы работаем с последовательным
портом вне области действия программы cu.
Попутно заметим, что временный выход (по команде escape) из cu
совершенно аналогичен временному выходу из любой команды системы
UNIX. Вы можете временно выйти для запуска определенной команды,
например:
~!echo "you can run any command here"
Вы можете также перейти в shell и работать так, как вы всегда
работаете:
~!sh
Вы должны, однако, помнить, что вы все еще подключены с помощью
программы cu к последовательному порту, а телефон по-прежнему
подсоединен к удаленной системе до тех пор, пока вы не дадите вручную
команду выхода или отключения телефона, или пока удаленная система не
отключит телефон по тайм-ауту.
ЗАХВАТ ДАННЫХ
Теперь мы умеем найти модем, подключиться к последовательной
линии и установить связь с другой системой. Если мы вызываем доску
объявлений, нам может понадобиться захватывать файлы с целью
последующего использования. Программа cu не предоставляет такой
возможности. Как мы уже отмечали, микро-ЭВМ делают это обычно при
помощи RAM-буфера, который сохраняется на диске, когда он
заполняется. А UNIX не может действовать таким образом.
Простейший способ, придуманный мною для захвата данных из другой
системы - пропустить по конвейеру все, что выдается на терминал,
через команду tee системы UNIX. Весь ввод с вашей клавиатуры не
перехватывается, а все, что идет на стандартное устройство вывода
(stdout) попадает в выходной файл утилиты tee. Это выглядит так:
cu -ltty00 dir | tee файл_перехвата
Одно из последствий перехвата информации таким методом состоит в
том, что все символы возврата каретки, печатаемые из удаленной
системы, попадают в выводной файл команды tee. Вы обнаруживаете их в
виде "^M" в конце каждой строки файла. Избавиться от всех этих
возвратов каретки легко, попробуйте выполнить такую
последовательность команд редактора ed:
ed файл_перехвата
1,$s/^M//g
w
q
Смысл этих команд следующий. Для каждой строки во всем файле (1,
$) заменить возврат каретки (^M) на ничего (//). Сделать это для
произвольного количества символов возврата каретки в отдельной строке
("g" означает глобальную замену в строке). Вы можете превратить
control-M в заменяемую строку либо вводом символа обратной косой
черты (\) и настоящим нажатием на клавишу возврата каретки, либо в
редакторе vi использовать префикс control-V для разрешения ввода
управляющих символов. После редактирования запишите файл и выйдите из
редактора ed.
Конечно, все это вы можете для удобства оформить в виде
командного файла интерпретатора shell.
ИНСТРУМЕНТАЛЬНЫЕ СРЕДСТВА ДЛЯ КОММУНИКАЦИОННЫХ ЛИНИЙ
Две утилиты, которые мы здесь предлагаем, связаны с доступом к
последовательному порту. Когда линия освобождается, вы сразу же
узнаете об этом и можете управлять линией с помощью нескольких
нажатий клавиш.

ИМЯ: cuchk

cuchk Cu check - проверка свободной линии для cu
НАЗНАЧЕНИЕ
Опрашивает таблицу состояния процессов и ищет процессы cu. Когда
они завершаются, на ваш экран выдается сообщение и программа
прекращает работу.
ФОРМАТ ВЫЗОВА
cuchk - Сообщить, когда cu освободит линию
ПРИМЕР ВЫЗОВА
cuchk
ТЕКСТ ПРОГРАММЫ
1 :
2 # @(#) cuchk v1.0 Check for a free cu line Author: Russ Sage
Проверка свободной линии для cu
4 if [ "$#" -gt "0" ]
5 then echo "cuchk: too many arguments" "&2
6 echo "usage: cuchk" "&2
7 exit 1
8 fi
10 while :
11 do
12 ps -e | fgrep cu " /dev/null \
13 && sleep 5
14 || { echo "\ncu is free"; exit; }
15 done &
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ cuchk?
Похоже, что большинство систем UNIX скупы на последовательные
порты. Это становится важным моментом, когда имеется только один
выводной порт для шести или семи человек. Опыт показывает, что если
вы не получите доступ к порту сразу, то вам прийдется ждать снова и
снова и вы не будете знать, как долго собирается текущий пользователь
занимать определенный порт. Вместо того чтобы терять время на
постоянную проверку занятости порта, почему бы не поручить машине
сообщить вам, когда этот порт освободится?
ЧТО ДЕЛАЕТ cuchk?
При вызове программы cuchk она запускается в фоновом режиме. Она
управляется бесконечным циклом, обеспечивающим ее работоспособность.
Программа cuchk наблюдает за системой, ведя поиск среди всех активных
процессов. Если она обнаруживает процесс cu (который может работать с
интересующим нас портом, а может и нет), она засыпает на 5 секунд и
снова ведет поиск. Если нет процессов cu, функционирующих на машине,
она выдает сообщение "cu is free" ("cu свободна") и завершается.
Таким образом, мы не обязаны вручную следить за работой cuchk, она
сама запускается и сама завершается. Это имеет смысл, поскольку вся
ценность этой утилиты свелась бы на нет, если бы ее саму нужно было
периодически проверять!
ПОЯСНЕНИЯ
В строках 4-8 выполняется проверка на ошибки. Поскольку cuchk не
имеет никаких опций, в командной строке не должно быть никаких
параметров. Если параметры присутствуют, это считается ошибкой.
Строки 10-15 организуют вечный цикл while. Обратите внимание,
что амперсанд в строке 15 автоматически запускает cuchk в фоновом
режиме, ведь нет никакого резона выполнять эту программу в
приоритетном режиме.
В строке 12 выполняется команда "ps -e", которая выдает
информацию о состоянии всех значительных пользовательских процессов.
Эти данные пропускаются по конвейеру через fgrep. Утилита fgrep ищет
наличие символов "cu". Мы надеемся, что эти символы соответствуют
только процессам cu, которые мы ищем. Они могут, однако, относиться к
чему-то совершенно неожиданному, например "picuser" или к какому-то
аналогично построенному имени. Результат работы команды fgrep
направляется на устройство /dev/null, чтобы избавиться от лишней
информации. Нас интересует только статус завершения команды fgrep. Он
сообщает нам, найдено ли вхождение символов "cu" или нет.
В конце строки 12 присутствует символ обратной косой черты,
указывающий интерпретатору shell, что следующая физическая строчка
является на самом деле частью той же самой программной строки и что
нужно добавить ее к строке 12, а не выполнять самостоятельно.
Разбиение длинных строк на несколько меньших строк с помощью символов
обратной косой черты позволяет нам получать красиво оформленный,
более наглядный исходный код.
Строка 13 выполняется в том случае, если fgrep отработала
успешно (т.е. если она выдает нулевой статус завершения). Это
означает, что символы "cu" найдены, а значит процесс cu работает и в
настоящее время не доступен. Поэтому процесс cuchk засыпает на 5
секунд. Когда он пробуждается, выполнение продолжается со строки 10,
возобновляется вечный цикл и проверка процессов продолжается.
Если fgrep не находит символы "cu" (возвращается ненулевой
статус завершения), то выполняется строка 14, которая выдает
сообщение о том, что программа cu свободна, и работа программы
завершается. Таким образом, мы сразу же знаем, что линия cu открыта
для использования, поэтому мы можем поскорее занять ее.

ИМЯ: talk

talk Обращение к последовательному порту
НАЗНАЧЕНИЕ
Выполняет командную строку, подготавливающую последовательный
порт для общения с другой системой.
ФОРМАТ ВЫЗОВА
talk [-bBAUD] [-l] [-tTTY] [-u]
Опции:
-b установить новую скорость обмена
-l протоколировать все поступающие данные
-t использовать другой порт tty
-u использовать принятую в UNIX скорость обмена 9600
ПРИМЕР ВЫЗОВА
talk -b300 -t01 -l
Обратиться к последовательному порту tty01 на скорости 300 бод и
протоколировать выводные данные в текстовый файл.
ТЕКСТ ПРОГРАММЫ
1 :
2 # @(#) talk v1.0 Talk to the serial port Author: Russ Sage
Обращение к последовательному порту
4 BAUD="1200"
5 TTY="tty11"
6 PIPE=""
8 for ARG in $@
9 do
10 case $ARG in
11 -b*) BAUD="`echo $ARG|cut -c3-`";;
12 -l) echo "logging in /tmp/talk.$$"
13 PIPE="| tee /tmp/talk.$$";;
14 -t*) TTY="tty`echo $ARG|cut -c3-`";;
15 -u) BAUD="9600";;
16 *) echo "talk: invalid argument $ARG" "&2
17 echo "usage: talk [-bBAUD] [-l] [-tTTY] [-u]" "&2
18 echo " -b baud rate" "&2
19 echo " -l log the output" "&2
20 echo " -t use another tty" "&2
21 echo " -u 9600 baud to UNIX" "&2
22 exit 1;;
23 esac
24 done
26 eval cu -s$BAUD -l$TTY dir $PIPE
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ
ARG Аргумент из командной строки
BAUD Скорость обмена, которую нужно указать программе cu
PIPE Содержит строку, создающую конвейер для захвата данных
TTY Номер используемого порта tty
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ talk?
Регистрация в удаленной системе расширяет горизонты ваших
вычислительных возможностей. Учитывая, что UNIX становится все более
распространенной системой среди микро-ЭВМ, системы UNIX, пригодные
для связи, скоро будут везде. Кроме того, вы можете вызвать из вашей
системы UNIX много систем типа досок объявлений, функционирующих на
микро-ЭВМ.
Нам нужен хороший интерфейс для управления модемной линией и
облегчения ее использования. Нам необходимо захватывать данные, если
это возможно, изменять скорость обмена и менять терминальный порт,
если в этом есть потребность. Все это делает командный файл talk.
ЧТО ДЕЛАЕТ talk?
Программа talk генерирует командную строку, основанную на
подразумеваемой конфигурации. Эта конфигурация такова: 1200 бод,
подключение к терминальной линии /dev/tty11 (последовательный порт 1
в системе SCO XENIX), захват данных не производить. Все
подразумеваемые параметры можно изменить в тексте исходного файла, а
терминальная линия должна быть установлена с учетом специфики вашей
системы.
Если вы хотите изменить скорость обмена, укажите с помощью опции
-b нужную вам скорость (подразумевается, что это одна из стандартных
скоростей, поддерживаемых программой cu). Например,"talk -b2400"
устанавливает скорость 2400 бод. Указанная скорость передается
команде cu, которая непосредственно изменяет характеристики вашего
подключения.
Важно иметь в виду, что эта скорость должна быть определена в
файле L-devices.
Если вы хотите захватить все данные, выводимые на ваш экран,
включите опцию протоколирования, указав -l. Файл с захваченными
данными будет называться /tmp/talk.$$, где $$ - уникальный
идентификатора процесса вашего текущего интерпретатора shell. Это имя
выдается на экран при вызове данной опции, так что вы увидите его на
вашем экране. Уникальность имен файлов обеспечивается для того, чтобы
вы не потеряли по недосмотру регистрационные файлы от предыдущих
сеансов работы (или чтобы, по крайней мере, было очень мало шансов,
что это произойдет).
Если у вас факультативный последовательный порт с модемом или
если вы хотите подключаться к любой из возможных линий, примените
опцию -t. Вместе с этой опцией укажите номер tty, который будет затем
передан в команду cu. Это очень полезная опция, если у вас много
различных линий для использования cu.
Если вы не используете ваш последовательный порт для подключения
модема, а имеете прямое подсоединение к другой системе UNIX, опция -u
тотчас же установит скорость обмена 9600 бод. Заметим, что того же
эффекта можно было бы достичь, указав -b9600, но опцию -u проще
вводить и запоминать.
ПРИМЕРЫ
1. $ talk -l -t12
Подключиться к дополнительному последовательному порту и
захватывать данные, которые проходят на экране.
2. $ talk -u
Подключиться к основному последовательному порту с
использованием скорости 9600 бод. Не включать захват данных.
3. $ talk -b2400 -t04 -u
Внимание! Сначала опция -b устанавливает скорость 2400, но далее
следует опция -u, которая переустанавливает значение скорости на
9600.
4. $ talk -u -l -b4800
Снова будьте внимательны! Сначала линия подключается напрямую со
скоростью 9600 бод. Затем включается протоколирование. Наконец,
значение скорости меняется с 9600 на 4800. Если на самом деле линия,
к которой вы обращаетесь, работает на скорости 9600 бод, вы должны,
если это возможно, понизить скорость, нажимая ~%b для генерации
сигнала break.
ПОЯСНЕНИЯ
В строках 4-6 устанавливаются действия, выполняемые программой
talk по умолчанию. Эти действия можно полностью изменить путем
редактирования исходного файла. Главное достоинство этих умолчаний в
том, что если такую конфигурацию вы используете чаще всего, то вам
достаточно просто ввести "talk", без всяких опций.
Строка 4 инициализирует переменную BAUD значением 1200, это
значение по умолчание для скорости модема. Строка 5 присваивает
переменной TTY значение tty11, что соответствует первому
последовательному порту аппаратуры. Обычно нужно tty00, но SCO XENIX
имеет виртуальную консоль, причем tty02-tty10 являются отдельными
экранами, доступ к которым осуществляется с одного консольного
устройства. Если это не так в вашей системе, укажите здесь правильное
значение TTY. В строке 6 переменная PIPE инициализируется пустой
строкой, так как конвейер применяется для протоколирования данных, а
по умолчанию захват данных не выполняется.
Строки 8-24 представляют собой цикл, который перебирает все
аргументы командной строки. В строках 10-23 используется оператор
выбора среди опций для выполнения требуемых функций.
Строка 11 проверяет, начинается ли данная опция с символов -b,
после которых следует что-то еще. Если да, то символы, следующие
после -b, вырезаются и заносятся в переменную BAUD. Обратите
внимание, что именно поэтому вы должны набирать "-b2400", а не "-b
2400".
В строке 12 проверяется, хотите ли вы включить протоколирование.
Если да, то на экран выводится имя протокольного файла, а переменная
PIPE устанавливается так, чтобы направить стандартный вывод по
конвейеру программе tee системы UNIX. Программа tee - это
универсальная конвейерная программа расщепления, которую можно
использовать для посылки копии потока данных в определенный пункт
назначения, не воздействуя на основной конвейер. В данном случае мы
применяем программу tee для посылки данных, проходящих через
последовательный порт, на экран и в наш регистрационный файл
/tmp/talk/.$$. (Если вы представите себе водяную трубу с T-образным
стыком, то поймете, что происходит.)
Строка 14 проверяет, начинается ли опция с -t. Если это так, то
вырезается номер терминала. Здесь можно передать любое значение tty,
но не забывайте, что этот номер tty должен также присутствовать в
файле /usr/lib/uucp/L-devices. Благодаря этому файлу, программа cu
знает, что терминальное устройство подключено к разрешенному порту.
В строке 15 проверяется, собираетесь ли вы подключаться к
системе UNIX напрямую. Если да, скорость обмена автоматически
устанавливается на 9600 бод в целях повышения пропускной способности
терминала.
Строки 16-22 выполняют проверку ошибок. Если какая-либо опция,
переданная в командной строке, не соответствует никакому из
предыдущих образцов оператора case, этот факт фиксируется здесь,
выдаются сообщения об ошибках и командный файл завершается.
Строка 26 - это волшебная строка. Поскольку все shell-переменные
получили свои значения, мы можем использовать их в командной строке,
чтобы подставить в нее необходимые значения. Сначала применяется
команда eval. Она раскрывает все имена переменных, заменяя их на
соответствующие значения.
В данном случае мы формируем полную командную строку утилиты cu:
скорость берется из переменной BAUD, линия из TTY, а подсоединение
прямое. Если мы не ведем протоколирование данных, значение PIPE
нулевое и оно будет отброшено при синтаксическом разборе. Если же
переменная PIPE содержит команду для отправки данных в протокольный
файл, команда eval обеспечивает это, организовывая такой конвейер,
как мы указали.
ДОСТУП ИЗ UNIX В UNIX
Теперь давайте рассмотрим полновесное общение двух систем UNIX.
Имеется область, в которой UNIX опередила свое время. Система uucp
позволяет связать несколько машин воедино и создать то, что в
некоторых отношениях является виртуальным окружением, позволяющим вам
работать на любой машине. Функционирование такой сети основано на
удаленной регистрации в системе (cu(1)), дистанционном выполнении
команд (uux(1)), электронной почте (mail(1)), передаче файлов
(uucp(1), uucico(1)) и опознавании узла системы (uname(1) и
uuname(1)).
Поскольку мы занимаемся реализацией коммуникаций типа UNIX-UNIX,
давайте рассмотрим некоторые способы физического соединения
UNIX-машин в одну рабочую среду.
СВЯЗЫВАНИЕ UNIX-МАШИН
В рабочей обстановке машинные конфигурации постоянно меняются в
целях тестирования, из-за аппаратных изменений, перестановок и
переездов и т.д. Планировать конфигурацию ваших машин нужно так,
чтобы она была максимально гибкой, это поможет избежать хаоса и
разлада.
Ваши потребности влияют на то, как вы соедините различные
UNIX-машины. Если имеется блок определения приоритетного запроса к
порту (port contender), вам приходится иметь с ним дело. Одни линии
могут быть подсоединены напрямую, другие напрямую через блок определения
приоритетного запроса к порту, третьи через модемные коммутаторы.
Для того чтобы научиться иметь дело со всеми этими возможностями,
попробуем представить эти конфигурации в графическом виде.
Первый тип подключения - прямое подключение (см. рис. 8-7).
Слева показана вызывающая система, справа вызываемая. Вызывающая
система использует последовательный порт для вывода, поэтому на этом
порту не должно быть процесса getty. Инициирующая команда "cu -ltty00
-s9600 dir" обеспечивает подключение к последовательному порту tty00
на очень высокой скорости обмена. Прямые подсоединения могут
поддерживать такую скорость. Сам кабель должен быть выполнен в виде
нулевого модема (рассмотренного ранее в данной главе). В вызываемой
системе задействованы процессы getty, работающие со скоростью 9600
бод на входящей терминальной линии. Когда пользователь вводит
регистрационное имя, getty выполняет процесс login, запрашивающий
пароль, и если этот пароль верный, запускается shell.
Рис. 8-7. Прямое подключение одной системы UNIX к другой

DTE-1 DTE-2
+---------+ +---------+
| | | |
| UNIX 1 | | UNIX 2 |
| | Нулевой модем | |
| tty00 |
"| tty00 |
| | | |
| | | |
+---------+ +---------+
ДЕЙСТВИЯ ДЕЙСТВИЯ
1. Нет getty 1. getty 9600 tty00
cu -ltty00 -s9600 dir login имя_пользователя
sh
2. uucp файл 2. getty 9600 tty00
unix 2!~/user login uucp
uucico

При использовании uucp происходят аналогичные вещи. Команда uucp
генерирует процесс uucico, инициирующий вызов системы, показанной
справа. Регистрационная последовательность та же самая, за
исключением того, что вместо запуска интерпретатора shell в конце
этой последовательности действий запускается еще один процесс uucico,
который общается с вызывающим процессом.
Следующая конфигурация - прямое подключение через селектор порта
- показана на рис. 8-8. Здесь тоже кабель, идущий от DTE-1 к
селектору порта, должен быть нулевым модемом.
Рис. 8-8. Прямое подключение через селектор порта

+---------+ +----------+ +---------+
| | | | | |
| UNIX 1 | | | | UNIX 2 |
| | Нулевой | Селектор | Прямой | |
| tty00 |---------"| порта |--------"| tty00 |
| | модем | | кабель | |
| | | | | |
+---------+ +----------+ +---------+
ДЕЙСТВИЯ ДЕЙСТВИЯ
1. cu -ltty00 -s9600 dir 1. getty tty00 9600
login имя_пользователя
sh
2. uucp файл 2. getty 9600 tty00
unix 2!~/user login uucp
uucico

Селектор порта принимает любое количество входных линий и
переключает их на меньшее количество фиксированных входных линий
компьютера. Таким образом, можно получить доступ ко всем терминалам,
не имея входной линии, которая часто бы пустовала, поскольку она
предназначена определенному лицу. Регистрационная последовательность
в точности такая же, как и для прямого подключения, кроме каких-либо
дополнительных нажатий на клавиши, применяемых для прохождения через
селектор. Обычно возврат каретки активизирует линию, чтобы получить
регистрационную подсказку.
Последняя конфигурация (на рис. 8-9) представляет собой
дистанционное подключение двух систем UNIX. Каждое терминальное
устройство DTE соединяется со своим модемом прямым кабелем. DTE-1
вызывает DTE-2 либо вручную с помощью cu, либо с помощью утилиты
uucp, использующей uucico и программу набора телефонного номера.
Самое большое отличие в том, что это подсоединение работает на
скорости 1200 бод. Это значит, что либо DTE-2 запускает 1200-бодовый
процесс getty, либо если getty имеет скорость 9600, то вам нужно
сбросить ее. Для того чтобы понизить скорость, в программе cu
требуется ввести ~%b в качестве сигнала break. Или, если вы работаете
с помощью uucp, можно поместить строку BREAK в файле L.sys, чтобы
отправить ее в вызванную систему.
Рис. 8-9. Дистанционное соединение двух систем UNIX

+---------+ +---------+
| | | |
| UNIX 1 | ***** _||_ ***** | UNIX 2 |
| | Прямой * * _||_ * * Прямой | |
| tty00 |-------"* модем *-- || --* модем *-------"| tty00 |
| | кабель * * || * * кабель | |
| | ***** || ***** | |
+---------+ +---------+
ДЕЙСТВИЯ ДЕЙСТВИЯ
1. cu -ltty00 -s9600 dir 1. getty 1200 tty00
login имя_пользователя
sh
2. uucp файл 2. getty 1200 tty00
unix 2!~/user login uucp
uucico

ОБЛАСТИ ДЛЯ ПЕРЕДАВАЕМЫХ ФАЙЛОВ
Когда вы пересылаете файлы между машинами, uucp придерживается
определенных протоколов и стандартов. Один из них связан с тем,
откуда и куда могут поступать файлы. Чтобы свести возможную
неразбериху к минимуму и обеспечить определенную степень защиты,
создаются защищенный и публичный каталоги для хранения стоящих в
очереди заданий и переданных файлов.
Наиболее важен каталог /usr/spool/uucp. Он содержит LOGFILE, что
дает возможность с помощью команды "tail -f LOGFILE" заглянуть в
операции передачи во время их выполнения. Транзакции uucp и mail
попадают в этот каталог. Обычно транзакция состоит из управляющего
файла (C.*) и файла данных (D.*). Когда одна машина используется в
качестве центрального узла, ее каталог uucp может заполниться очень
большим количеством файлов. Необходимо обычное сопровождение и
постоянное слежение за файлами занятости (LCK* и STST*), чтобы быть
уверенным, что все работает правильно.
Следующий интересный каталог /usr/spool/uucppublic, чаще всего
известный под названием PUBDIR (это shell-переменная). Он содержит
каталоги, названные по имени каждого пользователя, чтобы хранить
файлы, проходящие транзитом с одной машины на другую. Большинство
каталогов имеют все права доступа, что обеспечивает другим
пользователям возможность копировать файлы. Я посчитал полезным
создать переменную среды интерпретатора shell, которая содержит
маршрутный префикс моего каталога в /usr/spool/uucppublic, т.е.
P=/usr/spool/uucppublic/russ. Теперь я могу обращаться к файлам,
вводя $P/*. Это значительно сокращает ввод и упрощает пересылку
файлов в этот каталог и из него.
Переменная PUBDIR поддерживается синтаксисом uucp. В данном
примере используется синтаксис uucp:
$ uucp * remote!~/user
Эта команда копирует каждый файл текущего каталога в систему
"remote", затем обозначение ~/ превращается в префикс
/usr/spool/uucppublic. Если в этой команде user не является
каталогом, копируемые файлы получают имя user, а не копируются в
каталог с именем user. Вы должны сами создать каталог, после чего
применить к нему команду chmod 777, чтобы в него можно было
копировать. Для справки отметим, что указание ^user превращается
программой uucp в $HOME/user, а ^/user превращается uucp в
$PUBDIR/user.
Еще один способ ввода команды с помощью shell-переменных
выглядит так:
$ uucp * remote!$P
Такая команда копирует все файлы в мой каталог PUBDIR. Если я
зарегистрировался в этой системе, я могу ввести:
$ ls -al $P
или
$ cd $P
$ ls -al
для того, чтобы увидеть все скопированные файлы.
Один из моментов, за которым вы должны следить,- разрушительные
командные файлы типа "uuclean". Эти программы обычно запускаются
процессом cron или некоторыми другими фоновыми программами. Они
проходят по всем областям системы, связанным с uucp, находят файлы, к
которым не было обращений определенный период времени, и удаляют их.
Это может быть катастрофическим, если вы используете PUBDIR в
качестве временной области хранения. Вот как может выглядеть одна из
таких "очищающих" операций:
PATH=/usr/bin:/bin
export PATH
cd /usr/spool/uucp
find C. D. TM. X. XTMP -type f -mtime +7 -exec rm {} \;
cd /usr/spool/uucppublic
find . -type f -mtime +7 -exec rm {} \;
Если в вашей системе работают такие очищающие командные файлы,
имеется несколько способов сохранения файлов от неумышленного
уничтожения. Первый способ - постоянно обрабатывать все файлы
утилитой touch, чтобы они не распознавались оператором find, который
ищет старые файлы. Это можно сделать так:
$ find $P -exec touch {} \;
Утилита touch обновляет дату доступа и изменения файла. Фигурные
скобки означают, что нужно поместить в них литеральное имя,
соответствующее оператору find. Вам желательно оформить это в виде
запланированного процесса, который запускается чаще, чем программа
очистки!
Другая стратегия - проанализировать очищающую программу (или
командный файл). Она запускается суперпользователем (root)? Если нет,
то команда "chmod 000 $P" может помешать ей вести поиск имен внутри
моего каталога. Если же программа очистки запущена
суперпользователем, то, конечно, никакие ограничения прав доступа ее
не остановят. В какое время она запускается? Могу ли я приказать ей
пройти мимо меня, не заглядывая в мои файлы? Что является стартовым
каталогом для очищающего командного файла? Углубляясь в эти вопросы,
мы можем собрать много информации о том, что делает этот командный
файл и какие действия мы можем предпринять, чтобы он нам не навредил.
Может показаться, что самый легкий способ - просто удалить
программу очистки (в предположении, что вы имеете на это право). Это,
однако, не способствует поддержке свободного пространства и чистых
каталогов. К тому же если вы хотите запретить очистку каталогов,
которые очень важны для вас, то вы должны нести ответственность за их
сопровождение.
СЕТЕВАЯ БЕЗОПАСНОСТЬ
Каталог uucppublic, как мы уже видели, содержит файлы,
передаваемые транзитом между системами. Обычно все подкаталоги в
uucppublic имеют режим доступа rwxrwxrwx. Это сразу же порождает
проблему защиты, ведь кто угодно может создать, разрушить или
изменить файлы в этих каталогах.
Такие права доступа должны соблюдаться для любого каталога,
который является источником или адресатом для файлов, передаваемых с
помощью uucp. Uucp требует, чтобы все промежуточные каталоги имели
разрешение на чтение и запись для всех пользователей. Если uucp берет
из каталога исходные файлы, должны быть обеспечены права на чтение.
Если же uucp записывает файлы в этот каталог, нужны права на запись в
каталог-адресат. Если вы хотите, чтобы файлы uucp попадали прямо в
ваш регистрационный каталог, вы должны разрешить запись в ваш каталог
для всех окружающих.
Широко открытый доступ на запись позволяет доставлять файлы
прямо к вашему порогу, но вы не знаете, кому вы открываете двери.
Если вы заботитесь о безопасности, вам не пон ПРИЕМЫ П.
может записать все, что угодно, в ваше рабочее пространство. Одно из
решений - разблокировать ваш регистрационный каталог, но
заблокировать все ваши подкаталоги, кроме тех, которые нужны для
uucp. Основная проблема при этом - так установить все права доступа,
чтобы каждый файл обрабатывался надлежащим образом.
Более простое решение - держать ваши каталоги для uucp за
пределами вашего регистрационного дерева каталогов. Это избавляет вас
от проблемы безопасности, но означает, что вы должны вручную
копировать файлы после их попадания в каталог туда, где вы хотите их
фактически разместить.
ОГРАНИЧИТЕЛЬНЫЕ ФАЙЛЫ
Когда удаленная система регистрируется в центральной системе с
помощью uucp, несколько файлов в центральной системе определяют,
какие возможности имеет удаленная система. Эти конфигурационные файлы
размещаются в каталоге /usr/lib/uucp.
Первый из таких файлов называется L.cmds. Он содержит имена всех
команд центральной системы, которые можно выполнить из удаленной
системы. Если удаленная система посылает команду посредством uux, то
команда выполняется только при условии, что имя этой команды
присутствует в файле L.cmds.
Следующий файл - USERFILE - определяет, к каким каталогам
центральной системы может иметь доступ удаленная система. Вы можете
ограничить пересылки одним каталогом или разрешить доступ к любому
файлу системы. По умолчанию в файле USERFILE имеется такая запись:
uucp, /
что позволяет пользователю uucp (подразумеваемому пользовательскому
имени процесса uucico) читать и писать файлы в любом месте дерева,
начиная от корня и двигаясь вниз, т.е. фактически во всей системе.
Это открывает лазейки в системе защиты, например:
uucp central!/etc/passwd /tmp
Здесь извлекается парольный файл из другой системы. С помощью
этого файла можно найти имена пользователей без паролей, и другие
люди могут прорваться в вашу систему. Более строгий файл USERFILE
выглядит так:
uucp /usr/spool/uucppublic /tmp
что ограничивает файловые пересылки только указанными каталогами. Это
препятствует предыдущей попытке пересылки парольного файла.
Последний файл L.sys, вероятно, наиболее важен для uucp с точки
зрения системы защиты. Он содержит имена узлов, телефонные номера,
регистрационные имена и пароли для всех удаленных систем, известных
центральной системе. Если бы какая-то часть этой информации была
доступна широкой публике, кто-нибудь смог бы выполнить пересылку с
помощью uucp из удаленной системы и претендовать на роль центральной
системы. Новая система HoneyDanber uucp в System V кое-что делает для
того, чтобы не давать удаленным системам перехватывать почту и
пересылки данных путем маскировки под другие удаленные системы. В
главе 9 более подробно рассматриваются вопросы безопасности программы
uucp и коммуникаций вообще.
В следующем примере показан файл L.sys, в котором определены два
разных вида систем: системы типа прямого подключения и удаленные
системы с номеронабирателем (dial-up systems).
remote Any ACU 1200 5551212 ogin:--ogin: uucp word: uucp
selector Any ACU 1200 5551213 \d--CLASS--CLASS A ogin:--ogin: uucp word:
uucp
direct Any tty00 9600 tty00 ogin:-@-ogin: uucp word: uucp
Запись для системы "remote" отражает, что это линия с набором
номера, может быть вызвана произвольное число раз ("any time"),
доступна через ACU (automatic call unit - автоматическое устройство
вызова) со скоростью 1200 бод и по номеру 5551212. Регистрационная
последовательность определена в виде регистрационного имени uucp и
пароля uucp. Слово "ogin" - не опечатка. Uucp использует подсказку
"ogin:" для того, чтобы отличить ее от обычной подсказки login
системы UNIX. Распознавание шаблона "ogin:" более надежно, чем
использование "Login:" или "login:".
Указание "Any" можно заменить на определенные интервалы времени,
если 24-часовый доступ не разрешен. Заметим, что обозначение "ACU"
соответствует записи ACU в файле L-devices (как рассмотрено ранее). В
следующей записи файла L-devices имена cua0 и cul0 связаны с
устройством, присоединенным к модему, в данном случае /dev/tty00:
ACU cul0 cua0 1200
Эту связь можно проверить с помощью команды "ls -li /dev/tty*
/dev/cul* /dev/cua*".
Система "selector" в нашем файле L.sys также имеет набор номера,
только подключается через коммутатор порта. Для общения с
коммутатором порта необходима дополнительная информация, которая
начинается с символов "\d". Поля в файле L.sys следуют в таком
порядке: "ожидание посылка ожидание посылка ...". Когда мы в первый
раз подключаемся к селектору порта, в нашу линию ничего не выводится.
Просто он так работает. Селектору нужен символ , чтобы стать
активным, а первое поле uucp означает ожидание. Как нам ничего не
дождаться и послать возврат каретки? Путем указания uucp ожидать
невозможный символ, например control-D (\d). Uucp никогда не получит
его, поэтому по тайм-ауту эта программа выдаст возврат каретки (-).
Если слово CLASS приходит назад, когда мы посылаем CR, то мы
отправляем символ "A", который означает класс системной
идентификации, указанный в данной записи. Когда связь
устанавливается, мы ищем "ogin:" в качестве регистрационной
подсказки. Если мы не находим ее, то посылаем символы возврата
каретки. Это может потребоваться по той причине, что инициирующий
процесс getty в системе A может быть настроен на 9600 бод. Тогда мы
должны будем послать либо символы возврата каретки, либо символы
break, чтобы сбросить скорость обмена до 1200 бод.
Последняя системная запись "direct" не использует ACU и
телефонный номер. Она собирается обращаться к терминальной линии
tty00 на скорости 9600 бод, чтобы получить регистрационную
последовательность. Здесь нет селектора порта, через который нужно
пройти, а есть просто прямая линия. В файле L-devices эта линия
описана так:
DIR tty00 0 9600
ОТЛАДКА КОММУНИКАЦИЙ
В тех случаях, когда uucp работает неправильно, можно
воспользоваться некоторыми хитростями отладки. Последовательность
действий по отладке обычно проходит по такому циклу: попытаться
передать файл, локализовать проблему, разрешить ее, сделать еще одну
передачу. Здесь мы рассмотрим локализацию проблемы и выполнение еще
одной пересылки.
Если после проверки и перепроверки всех конфигурационных файлов
имеют место отказы при передаче файлов, запустите вручную программу
/usr/lib/uucico с некоторыми активными отладочными флагами. Они
помогут увидеть, как происходит рукопожатие. Вот командная строка для
отладочного режима:
/usr/lib/uucp/uucico -r1 -x9 -ssystem_name
где r1 указывает программе uucico стартовать в ведущем (master)
режиме, вызывая system_name, а x9 указывает уровень отладочных
сообщений. Если вы хотите получать от uucico поменьше подробностей,
можете понизить уровень отладки до x4 или другого значения.
Отладочный вывод варьируется в диапазоне от x1 до x9.
Типичная последовательность для этой команды выглядит так. Во
-первых, поставить некоторые файлы в очередь для того, чтобы
направить их в другую систему. Благодаря постановке файлов в очередь,
вы избегаете автоматического запуска процесса uucico. После того как
файлы поставлены в очередь и готовы к передаче, запустите отладочный
режим и следите за происходящим. Это выглядит примерно так:

|
| $ uucp -r *.c remote!~/src
| $ /usr/lib/uucp/uucico -r1 -x4 -sremote
|
Если вам нужно только активизировать передачу с помощью uucp в
обычном режиме, то проще всего применить команду mail. Направьте
почтой некоторый текст пользователю другой системы - и механизм uucp
сразу же начнет работу. Следующая команда приводит к тому, что этот
механизм вызывает систему "remote" и запускает утилиту rmai(1) в
другой системе, чтобы передать по почте файл "dummy" пользователю
"user":
$ mail remote!user " dummy
Есть команда, которая явно вызывает /usr/lib/uucp/uucico - это
команда uusub(1M), размещенная в каталоге /usr/lib/uucp. Она
вызывается с указанием имени системы, с которой вы хотите связаться.
Большинство систем имеют такую команду. Если у вас ее нет,
пользуйтесь uucico. Если такая команда есть, вы имеете еще один
способ запуска uucico. Синтаксис выглядит так:
$ /usr/lib/uucp/uusub -c system
Теперь когда мы знаем, как использовать все эти команды вручную,
мы можем рассмотреть некоторые инструментальные средства,
автоматизирующие большую часть вашей работы с uucp.

ИМЯ: uust

uust Состояние uucp и служебные действия
НАЗНАЧЕНИЕ
Предоставляет управляемый с помощью меню доступ ко многим
служебным функциям, связанным с утилитой uucp и передачей файлов.
ФОРМАТ ВЫЗОВА
uust
Опции меню:
c - подключиться к другой системе в отладочном режиме
d - показать файлы в вашем каталоге PUBDIR
f - длинноформатный список файлов в каталоге подкачки
(spool directory)
l - динамически отображать регистрационный файл
r - повторное подключение к другой системе
s - дать пользователю сводку о транзакциях
u - разблокировать все терминальные линии (ОСТОРОЖНО:
это может нарушить сеанс работы)
w - отобразить регистрационный файл за последнюю неделю
ПРИМЕР ВЫЗОВА
uust Динамически отображать регистрационный файл, чтобы
следить за транзакциями uucp
ТЕКСТ ПРОГРАММЫ
1 :
2 # @(#) uust v1.0 Uucp status utility Author: Russ Sage
4 if [ $# -gt 0 ]
5 then echo "uust: argument error" "&2
6 echo "usage: uust" "&2
7 exit 1
8 fi
10 UUNODE=`uuname -l`
11 echo "
12 UUST MENU system node: $UUNODE
13 ---------
14 c - connect with another system in debug mode
15 d - show files in your directory under PUBDIR
16 f - long list files in the spool directory
17 l - display the logfile dynamically
18 r - reconnect with another system
19 s - give user summary of transactions
20 u - unlock the tty line
21 w - display logfile for the last week
22 - exit program
24 Press c,d,f,l,r,s,u,w,or : \c"
25 read CMD
27 case $CMD in
28 "") exit 0;;
29 c) echo "\nSystem name ( to exit): \c"
30 read SYSTEM
31 if [ "$SYSTEM" = "" ]
32 then exit 0
33 fi
34 echo "\nrm /usr/spool/uucp/STST.$SYSTEM : \c"
35 rm /usr/spool/uucp/STST.$SYSTEM 2"/dev/null \
36 && echo "" || echo "no STST files"
37 echo "\n/usr/lib/uucp/uucico -r1 -x4 -s$SYSTEM:"
38 /usr/lib/uucp/uucico -r1 -x4 -s$SYSTEM;;
39 d) echo "\n/usr/spool/uucppublic/$LOGNAME:"
40 ls -l /usr/spool/uucppublic/$LOGNAME;;
41 f) echo "\n/usr/spool/uucp:"
42 ls -l /usr/spool/uucp | more;;
43 l) echo "\n/usr/spool/uucp/LOGFILE:"
44 tail -20f /usr/spool/uucp/LOGFILE;;
45 r) echo "\nSystem name ( to exit): \c"
46 read SYSTEM
47 if [ "$SYSTEM" = "" ]
48 then exit 0
49 fi
50 echo "\nrm /usr/spool/uucp/STST.$SYSTEM : \c"
51 rm /usr/spool/uucp/STST.$SYSTEM 2"/dev/null \
52 && echo "" || echo "no STST files"
53 echo "uusub -c$SYSTEM:"
54 if [ -f /xenix ]
55 then /usr/bin/uusub -c$SYSTEM
56 else /usr/lib/uucp/uusub -c$SYSTEM
57 fi
58 tail -20f /usr/spool/uucp/LOGFILE;;
59 s) echo "\nuulog -u$LOGNAME:"
60 uulog -un$LOGNAME | more;;
61 u) echo \\nrm /usr/spool/uucp/LCK* :
62 rm /usr/spool/uucp/LCK* 2"/dev/null || echo "no lock files";;
63 w) echo "\n/usr/spool/uucp/Log-WEEK:"
64 more /usr/spool/uucp/Log-WEEK;;
65 *) echo "uust: invalid argument '$CMD'" "&2;;
66 esac
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ
CMD Содержит символ входной команды, поступивший из
стандартного ввода
LOGNAME Содержит ваше регистрационное имя
SYSTEM Узловое имя uucp той системы, в которую вы обращаетесь
UUNODE Узловое имя uucp локальной системы
ОПИСАНИЕ
ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ uust?
Многие области системы UNIX являются фактически самостоятельными
подсистемами. Uucp одна из них. Она использует конфигурационные
файлы, файлы подкачки (spool files), файлы блокировки (lock files) и
имеет много исполняемых модулей, образующих целую систему. Если вы
интенсивно используете uucp, вы часто занимаетесь просмотром
регистрационных файлов, очисткой каталогов после аварийных завершений
работы uucp и наблюдением за системой в целом. Если делать все это
вручную, требуется запоминать каталоги, файлы, команды, опции. Трудно
запомнить так много символов для ввода команд. Uust значительно
сокращает издержки и облегчает использование утилиты uucp.
ЧТО ДЕЛАЕТ uust?
Uust - это управляемый с помощью меню интерфейс, предоставляющий
основные служебные функции, необходимые для среды uucp. Вам нужно
набрать только команду uust, а затем требуемые опции.
Вполне возможно также, что вы обнаружите трудосберегающие
функции, о которых вы не знали раньше.
Первое, что печатает данные командный файл - это главное меню:

|
| UUST MENU system node: russ
| ---------
| c - connect with another system in debug mode
| d - show files in your directory under PUBDIR
| f - long list files in the spool directory
| l - display the logfile dynamically
| r - reconnect with another system
| s - give user summary of transactions
| u - unlock the tty line
| w - display logfile for the last week
| - exit program
|
| Press c,d,f,l,r,s,u,w,or :
Первая опция 'c' предназначена для подключения линии uucp в
отладочном режиме. Эта опция генерирует такую же командную строку,
как при ручном способе запуска процесса uucico (см. предыдущий
раздел). Uust запрашивает у вас имя системы, к которой вы хотите
подключиться.
Следующая опция 'd' дает длинноформатный листинг файлов вашего
каталога, определяемого вашей переменной $PUBDIR, т.е.
/usr/spool/uucppublic/$LOGNAME. Если вы часто делаете пересылки с
помощью uucp, здесь может собраться много файлов. Данная опция
предоставляет вам простой способ их просмотра.
Опция 'f' выдает длинноформатный листинг всех файлов,
находящихся в каталоге подкачки. Выход команды "ls -l" передается по
конвейеру команде more, что удобно при наличии большого количества
файлов. Этот каталог является сердцем исполняющей системы uucp. Почти
все находится или в этом каталоге, или как-то связано с ним.
Опция 'l', видимо, наиболее часто используемая во всей утилите.
Она позволяет увидеть в динамике функционирование uucp, наблюдая за
регистрационным файлом. При первом вызове она печатает последние 20
строк этого файла, а затем ведет наблюдение, пока не будет
остановлена ее работа.

Поділитися у соцмережах

☏ Замовлення реклами: +3805044-68-872

© Асоціація електронних бібліотек України

☝ Усі матеріали сайту (зокрема статті, зображення, рекламні оголошення тощо) призначені лише для попереднього ознайомлення. Усі права на публікації, представлені на сайті, належать їхнім законним власникам. Просимо Вас не зберігати копії інформації.