ЭЛЕКТРОННАЯ БИБЛИОТЕКА КОАПП
Сборники Художественной, Технической, Справочной, Английской, Нормативной, Исторической, и др. литературы.




     Рас Сэйдж.
     Приемы профессиональной работы в 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

     Этот бесконечный цикл будет выполняться постоянно.  Если