Оцените этот текст:


 Перевод "Tricks of the UNIX Masters" by Russel G. Sage
---------------------------------------------------------------
 From: Владимир Казеннов 
---------------------------------------------------------------

    * ВВЕДЕНИЕ *

Непрерывное снижение цен, рост производительности в наше время и ожидаемое появление новых микро- и супер-микрокомпьютеров делают мощь системы 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 18 fi 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 заставляет снова печатать главное меню вместо того, чтобы пытаться выполнить какую-то наполовину определенную операцию копирования. В строке 71 мы получаем для проверки команду главного меню и ин- формацию, необходимую для ее обработки. Четырьмя основными командами являются копирование, восстановление, выдача списка файлов и выдача списка файлов с полной информацией. Если введена какая-то другая коман- да, выдается сообщение об ошибке и главное меню снова выводится на эк- ран. Единственный способ выхода из главного меню - это нажать возврат каретки без какого либо-текста перед ним и строка 28 позволит выйти из цикла. Команды копирования и восстановления используют относительное име- нование. Сначала они требуют указать каталог, затем переходят в этот каталог. Оператор echo и "холостое" чтение переменной CMD просят поль- зователя вставить дискету (или смонтировать магнитную ленту или еще что-нибудь) и нажать возврат каретки, когда все готово. В случае копирования для поиска ВСЕХ файлов, размещенных в дереве файлов, начиная с текущего каталога, используется команда find. Опера- тор find выдает отсортированный список файлов, поэтому файлы на носите- ле с копией отсортированы. Затем отсортированный список файлов переда- ется по каналу команде cpio с опциями -ocBv. Это означает: "потоковый вывод, использовать символьные заголовки, блоки размером по 5K, с выда- чей сообщений". При этом печатаются имена файлов по мере того, как они копируются на носитель. В случае операции восстановления используются ключи -icBvdmu. Это значит "потоковый ввод, использовать символьные заголовки, блоки по 5К, с выдачей сообщений для печати имен файлов по мере их восстановления, создавать каталоги при необходимости, файлы сохраняют исходную дату мо- дификации, и все файлы безусловно копируются". Если должен быть выдан только список файлов, то ключами будут или -icBt для печати таблицы скопированных файлов (это соответствует записи команды cpio "ls"), или -icBtv для печати таблицы файлов с более под- робной информацией ("ls -l" в записи для cpio). В конце выполнения каждой команды главного меню выдается сообщение hit (Нажмите <ВК>) Это сделано по той причине, что когда печатается главное меню, оно очищает экран. Если бы вы хотели получить список файлов, как описано выше, и не завершить работу, то напечатался бы список, выполнение достигло бы внешнего оператора "done", внешний цикл снова стартовал бы и экран очистился бы перед новой выдачей на него главного меню. Уф, на- конец появился список, даже до того как Эвелин Вуд с высшим образовани- ем смогла прочитать это! Для того, чтобы задержать очистку экрана, мы ожидаем нажатие на клавишу. Что бы ни было введено, оно читается в пе- ременную и снова никогда не используется. Это просто холостая перемен- ная. Замечания по операции копирования Вы можете производить копирование файлов многими путями, но давай- те рассмотрим некоторые отличия между командами cpio и tar. Первона- чально командой копирования в UNIX была команда tar. Эта утилита созда- ния копии на магнитной ленте была предназначена для ведения архивов на магнитной ленте и выполнения самого копирования. Она работает, но имеет некоторые особенности. Во-первых, каждый файл, помещаемый на ленту (или какой-либо носитель, который вы используете), выравнивается на границу килобайта. Это означает, что если ваш файл состоит из одного байта, ко- манда tar выделяет минимум 1К вашему файлу, что может привести к значи- тельной растрате пространства, если у вас много небольших файлов и вы копируете их на магнитную ленту. Однако команда tar имеет также ряд неплохих аспектов. Например, вы можете сказать ей, какой множитель блокировки вы используете и насколько велик образ копии, так что вы можете разбить большие копируемые потоки данных на много мелких частей (например k=360 для гибких дисков низкой плотности в системе XENIX). Одним из странных аспектов команды tar является то, что копия является одним длинным и непрерывным потоком, а при восстановлении используется уникальный фор- мат для каждого носителя. Например, вы должны, скажем, скопировать 10M данных. Вам лучше иметь достаточно отформатированных гибких дисков и приготовить их до того, как вы начнете копировать командой tar. Когда дискета заполнится, вы должны прервать выполнение команды и затем снова продолжить. Пример такой команды мог бы выглядеть так: cd $HOME tar cvefbk /dev/fd048ds9 18 360 . Здесь указано, что требуется скопировать ВСЕ файлы (рекурсивно об- ходя дерево сверху вниз) из текущего каталога (.) в файл на указанном устройстве, со множителем блокировки 18 K и размером образа копии 360 Кбайт. Одним из интересных аспектов здесь является то, что когда коман- да tar рекурсивно проходит вниз по дереву, она получает имена файлов в порядке расположения описатель файла, что обычно НЕ совпадает с отсор- тированным порядком. Вы НЕ МОЖЕТЕ получить отсортированный список фай- лов для копирования командой tar, если только не сделаете еще одну точ- ную копию всех данных, которые вы хотите скопировать и не разместите описатель файла в отсортированном порядке. Как это сделать? Вот так: cd $HOME find . -print | sort | cpio -pdv /bkpsort При этом получится новая копия всех ваших данных и имена файлов разместятся в отсортированном порядке. Если после этого вы перейдете в каталог /bkpsort и выполните команду tar, файлы будут перенесены на носитель в отсортированном порядке. Предположим, что для выполнения копирования требуется 15 дискет. При восстановлении этого полного набора вы должны вводить приведенную ниже команду 15 раз, поскольку на каждом из гибких дисков располагается уникальный образ копии. tar xvf /dev/fd048ds9 Такие повторные вводы команды раздражают, но они могут облегчить жизнь, что мы вскоре и увидим. Команда cpio является следующим поколением команд копирования. Ее общие функции подобны функциям команды tar, но имеется несколько важных отличий. Во-первых, вы должны сгенерировать список файлов для cpio, что означает использование команды find для порождения списка. Поскольку мы можем конвейером пропустить список, полученный от команды find, через команду sort, нам нет необходимости делать еще одну копию всех наших файлов для получения отсортированного списка. Далее, команда cpio не выполняет выравнивание границу килобайта. Она пакует все данные непрерывно и имеет магическое число в заголовке для указания начала каждого нового файла. В команде cpio также нет ука- зания размера образа на носителе. Как она это узнает? Драйвер уст- ройства должен определить размер и послать соответствующий сигнал об- ратно команде cpio, которая после этого приостанавливается и предлагает вставить следующую дискету. Это все прекрасно до тех пор, пока вы не попадаете в систему, в которой драйверы ужасны, как в системе XENIX. Драйверы XENIX не распознают, когда нужно остановиться и продолжают вы- полнять работу и после того, как достигнут конец гибкого диска. Прог- рамма cpio должна была бы выполняться одинаково на всех системах, но она не работает корректно на машинах с системой XENIX. Одно существенное различие между командами cpio и tar заключается в получающемся образе копии. Поскольку cpio не различает границ между различными носителями (дискетами), файлы получаются разорванными между двумя гибкими дисками. Это значит, что когда вы пытаетесь копировать с этих 15 дискет, то они являются ОДНИМ непрерывным потоком входных дан- ных, точно так, как и последовательный поток выходных данных при созда- нии этой копии. Что произойдет, если дискета номер 2 повредится? ВСЕ ваши файлы после второй дискеты стали бесполезны. Вы просто потеряли весь ваш образ копии. Поскольку tar копирует в виде отдельных образов, когда дискета номер 2 потеряется, вы все равно можете копировать с дискет 3-15 без проблем. Еще один прекрасный аспект команды cpio заключается в том, что она может работать как в формате файловой системы, так и в потоковом форма- те. Формат файловой системы (опция -p) обращается к блочным уст- ройствам, таким как жесткий диск, а потоковый формат обращается к нест- руктурированным устройствам (опции -i и -o), таким как магнитная лента или гибкий диск с форматом низкого уровня. Cpio - это прекрасная утили- та для использования ее при копировании файловых деревьев системы на жестком диске. Как же управляется с этим система 4.2 BSD? В течение многих лет применялась команда tar для пересылки туда и обратно, как описано на страницах руководства по tar(1). Не самый элегантный подход, но рабо- тоспособный. Сейчас они имеют ключ -r (для рекурсивного обхода дерева сверху вниз) для обычной команды cp. Я же по-прежнему считаю, что ко- манда cpio лучше. 3.4. Средства проверки операций копирования 3.4.1. dsum - контрольные суммы двух катологов ------------------------------------------------------------- Имя: dsum _____________________________________________________________ dsum Контрольная сумма двух каталогов

    НАЗНАЧЕНИЕ

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

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

dsum [-c|-o] control_dir backup_dir Пример вызова dsum $HOME/bin /mnt Просматривает, были ли какие-либо файлы изменены при копировании из моего регистрационного каталога на гибкий диск, смонтированный в ка- талоге /mnt. Командный файл dsum 1 : 2 # @(#) dsum v1.0 Dual directory sum Author: Russ Sage 4 if [ $# -lt 2 -o $# -gt 3 ] 5 then echo "dsum: invalid argument count" >&2 6 echo "usage: dsum [-c|-o] control_dir backup_dir" >&2 7 echo " -c = C source files, -o = object files" >&2 8 exit 1 9 fi 11 case $# in 12 2) FLIST=*;; 13 3) case $1 in 14 -c) FLIST=*.c;; 15 -o) FLIST=*.o;; 16 *) echo "dsum: invalid argument $1" >&2 17 echo "usage: dsum [-c|-o] control_dir bacup_dir" >&2 18 exit 1;; 19 esac 20 shift;; 21 esac 23 for FILE in $1/$FLIST 24 do 25 BASEF=`basename $FILE` 26 if [ `expr $BASEF : '.*'` -lt 7 ] 27 then echo "`$BASEF: \t'`sum $FILE | cut -d' ' -f1`\t\c" 28 else echo "$BASEF:\t`sum $FILE | cut -d' ' -f1`\t\c" 29 fi 30 sum $2/$BASEF | cut -d' ' -f1 31 done Переменные среды выполнения BASEF Содержит базовое имя файла из полного маршрутного имени FILE Содержит имя каждого проверяемого файла FLIST Содержит указание на тип проверяемых файлов Описание Зачем нам нужен dsum? В среде разработки программ всегда имеется масса файлов. Эти файлы содержат все: исходный код, перемещаемые модули, объектный код, данные, тексты. Другим аспектом среды разработки программ является то, что эти файлы обычно рассыпаны по многим различным машинам (или группам машин, может быть и такой случай). В этом случае всегда кажется, что имеется очень много перемещений файлов: эти файлы передаются из одной системы на другую, некоторые модифицируются, пересылаются обратно и так далее. Похоже на то, как в армии роют ямы: вы делаете это, потому что вам при- казано. Когда вы перемещаете много файлов, то какой путь является лучшим для того, чтобы гарантировать себе (или кому-либо еще), что выполненная вами копия является ТОЧНО такой, как и оригинал? Если вы внесли ошибку в первоначальную копию, затем распространили эту ошибку на многие копии или даже записали вместо оригинала модифицированную копию, то вы можете никогда не вернуться в первоначальное состояние. Одним из способов слежения за копиями является использование ко- манды sum. Эта команда читает данные и выводит число, являющееся разно- видностью контрольной суммы. Другими утилитами UNIX, которые делают что-то подобное, являются cmp для сравнения объектных файлов и diff для обнаружения различий в текстовых файлах. Автор привык думать, что sum будет сообщать об отличии даже в од- ном бите (своего рода циклическая избыточная проверка), но это оказа- лось совсем не так. Недавно имелся 35 Кбайтный файл, содержащий в виде длинного формата список файлов, которые должны были быть скопированы. В действительности, там были два файла, один из которых был отсортирован, а другой нет. Они были одного размера, и sum выдала одно и то же число для обоих файлов. Когда же cmp сравнила эти два файла, оказалось, что 39-е байты отличаются. Как мы можем объяснить тот факт, что sum рассматривала эти два файла как совершенно одинаковые? Возможно, sum свернула эти два файла таким образом, что контрольная сумма оказалась одинакова, даже хотя один из файлов был отсортирован, а другой нет. Это значит, что sum на самом деле не выполняет контрольную провер- ку каждого бита. Только проверка алгоритма работы программы в исходном модуле позволит убедиться в этом. Конечно, в большинстве случаев, если файл отличается от оригинала, то это не является простой перестановкой данных, так что sum все-таки полезна. Что делает dsum? Dsum - это утилита, которая выполняет проверку после копирования. Она предполагает, что файлы скопированы из каталога-источника в ката- лог-приемник. Каталог-источник назван управляющим каталогом, поскольку он следит за тем, какие файлы сравниваются. Для каждого файла в управ- ляющем каталоге печатается его имя вместе со значением его контрольной суммы и со значением контрольной суммы для скопированного файла в ката- логе-приемнике. Вся эта информация выдается в одной строке. Польза от получения всей информации от dsum в одной строке заклю- чается в том, что визуально два файла могут быть проверены очень легко. Вам нет необходимости смотреть в другое место для получения необходимой информации. Альтернативой для dsum может быть выполнение какого-либо сценария, подобного приводимому ниже. 1. Скопируйте ваши файлы в другой каталог. 2. Подсчитайте контрольную сумму всех файлов из управляющего каталога и выведите результат в какой-либо файл. 3. Подсчитайте контрольную сумму всех файлов в каталоге, содержащем копию, и выведите результат в какой-либо файл. 4. Сравните эти два файла командой diff для того, чтобы увидеть, не отличаются ли какие-либо копии. Это не дает вам даже хорошего вывода на экран о том, что происхо- дитгиЭто не дает вам даже хорошего вывода на экран о том, что происхо- Dsum не проходит вниз по дереву файлов, потому что большинство ко- пий являются копиями из каталога в каталог, а не из сегмента дерева в сегмент дерева. Из-за того, что она не выполняет такой обход, сложность программы существенно понижается. По умолчанию сравниваются ВСЕ файлы. Это предполагает, что вы ско- пировали все файлы в каталог копирования. В некоторых случаях вы можете захотеть копировать только выбранные файлы, такие как *.c (все ваши исходные файлы). В этом случае управляющий каталог содержит множество файлов, а каталог с копиями содержит только файлы с расширением .c. Для поддержки таких случаев в программу включены ключи -c и -o. Ключ -c указывает только файлы типа *.c из управляющего каталога. В ре- зультате производится проверка только файлов *.c в каталоге с копией. Ключ -o выполняет то же самое для файлов, соответствующих *.o. Примеры 1. $ mount /dev/fd0 /mnt $ cp /usr/include/* /mnt $ dsum /usr/include /mnt Монтирует гибкий диск в каталог /mnt. Копирует все файлы заголов- ков в каталоге /usr/include на гибкий диск. Проверяет копии, используя dsum для исходного каталога и для каталога с копией. Примечание: Указывая копировать *, мы вообще не попадем в каталог /usr/include/sys. 2. $ dsum . .. Используя в качестве управляющих файлов файлы в текущем каталоге, сверить каждый файл с одноименным файлом в родительском каталоге. Пояснения В строках 4-9 производится проверка на наличие ошибок. Если указа- но менее двух аргументов, значит управляющий каталог и/или каталог ко- пии не указан и в результате обнаруживается ошибка. Если количество ар- гументов превышает три, значит, указано еще что-то кроме ключа -c и двух каталогов, что также является ошибкой. Все остальное (два или три аргумента) рассматривается как допустимое значение. В строках 11-21 производится инициализация переменной FLIST. FLIST - это управляющая переменная, которая определяет имена файлов, на кото- рые надо обратить внимание. Если в командной строке указаны только име- на каталогов ($# = 2), FLIST присваивается значение по умолчанию * (все файлы) в строке 12. Значение * присваивается переменой FLIST и не трак- туется в это время как метасимвол (это особенность командного процессо- ра). Если в командной строке указан ключ ($# = 3), производится провер- ка первой переменной и FLIST получает соответствующее значение, *.c или *.o. Если указана не такая опция, выводится сообщение об ошибке и прог- рамма завершается. В строках 23-31 выполняется сама работа. Здесь выполняется цикл for, который проходит по списку слов, созданному управляющим каталогом в соответствии со значением переменной FLIST. В строке 23 переменная FLIST расширяется фактически с символа * в имя каждого файла. Тем самым цикл for получает данные для использования. Следовательно, переменная FLIST является полным маршрутным именем каждого файла в управляющем ка- талоге. Строка 25 разбирает расширение, сделанное в строке 19. Переменная BASEF получает базовое имя полного маршрута из переменной FILE. Причи- ной, по которой мы это делаем, является тот факт, что позже при ссылке на каталог копии нам необходимо только имя файла. (В системе UNIX ко- манда basename возвращает последний элемент в указанном маршруте, т.е. само имя файла, если маршрут содержит промежуточные каталоги.) Строки 26-29 выводят первую часть выходного сообщения. Оператор if-then использован потому, что нам нужно менять выходное сообщение в зависимости от того, сколько символов содержит имя файла. Строка 26 оп- ределяет длину имени файла, используя команду expr. Команда expr может быть использована для сравнения двух строк и получает количество сов- павших символов. Сравнение имени файла со "всеми символами" (*), таким образом возвращает длину строки. (У вас может возникнуть желание обра- титься к expr(1), чтобы получить информацию о других хитростях этой многоцелевой команды.) Это возвращаемое значение используется в операторе test для опре- деления, содержит ли имя файла менее семи символов: возможно всего один или два символа. В последнем случае, если мы делаем табуляцию, мы полу- чим только первую позицию табуляции. Для получения последующих табуля- ций мы отображаем семь символов для того, чтобы попасть на место следу- ющего поля табуляции. (Если было 3-6 символов, мы все равно остановимся на поле второй табуляции, т.е. это место работает верно.) Затем отобра- жаем табуляцию для того, чтобы мы попали на место окончания второй та- буляции, что нам и требовалось. Если имя файла содержит более семи символов, мы уже находимся в первой позиции табуляции или за ней. Таким образом, следующий символ табуляции передвинет нас во вторую позицию табуляции. Эффект заключа- ется в том, что для размещения колонок не имеет значения размер имени файла (кроме случая, когда оно действительно очень длинное). Это позво- ляет избавиться от "блюза ползущих колонок", когда колонки сдвигаются в зависимости от размера отображаемой информации. В качестве примера та- кого эффекта может служить стандартная команда sum. Ее выход выглядит так: -------------------------------- | 4243 3 autobkp | 247 1 can | 25167 6 cpiobr | 186 3 dosflp | 56864 2 dsum | 2782 1 log | С другой стороны, выход dsum очень ясный и четкий, не сдвигается по всему экрану. Сдвиг делает вывод изломанным и затрудняет быстрый просмотр информации. Чудо вывода в одну строку совершается в строке 27 (для файлов с именами менее 7 символов) или в строке 28 (для файлов с более длинными именами). Внутри команды echo в каждом случае мы прячем другие команды, но по-прежнему управляем тем, как их результаты выводятся на экран. Во-первых, сами имена файлов выводятся, будучи ранее извлеченными из полного маршрутного имени. Обратите внимание, что имена файлов не со- держат информацию о том, из какого они каталога. Затем мы несколько сдвигаемся и печатаем первое поле выходной суммы для этого файла (саму контрольную сумму). Это контрольная сумма версии файла в управляющем каталоге, поскольку переменная FILE была сгенерирована для этого ката- лога. Команда sum выводит три значения (контрольная сумма, число бло- ков, занятых файлом, и само имя файла). Нам нужно получить только пер- вое значение, которое извлекается путем выполнения команды sum и пере- дачи ее выхода по каналу команде cut, которая и возвращает первое поле. После того, как значение контрольной суммы напечатано, мы отображаем \c для запрещения перехода на новую строку. Это сохраняет курсор в той же строке. Здесь начинает работать строка 30. Она генерирует контрольную сум- му того же файла в каталоге копии ($2 в командной строке) и текущее имя файла, вырезая только число, и печатает его правее курсора в той же строке. Цикл завершается, когда все файлы из управляющего каталога будут проверены командой sum.

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

Неплохой модификацией может быть вариант, чтобы dsum печатала не только значения, как это сделано, но и флаг в конце строки в случае, когда два файла отличаются. Тогда у нас не было бы необходимости просматривать непосредственно числа. Мы ищем флаг и сразу видим, когда что-то не так. Ниже приводится решение проблемы вывода флага для того, чтобы об- легчить вам работу. Оно не включено в представленную выше утилиту по той причине, что такого рода вещи могут быть выполнены относительно просто. Вы можете сами сделать эту модификацию в качестве упражнения. Текст программы, выполняющей эту работу, выглядит так: for FILE in $1/$FLIST do BASEF=`basename $FILE` S1=`sum $FILE 2>&1 | cut -d' ' -f1` S2=`sum $2/$BASEF 2>&1 | cut -d' ' -f1` if [ "$S1" = "$S2" ] then M="" else M="<---" fi if [ ` expr $BASEF : '.*'` -lt 7 ] then echo "$BASEF: \t$S1\t$S2 $M" else echo "$BASEF:\t$S1\t$S2 $M" fi done Подход к решению немного отличается от решения, принятого при на- писании dsum, поскольку вы не можете генерировать контрольную сумму на ходу. Вы должны перехватить выход команды sum и использовать его позже. То, что мы ищем, появляется в шестой строке. Если две контрольные суммы различны, переменная M устанавливается соответствующим образом. Если файлы различаются, переменная M получает стрелку, указывающую на то, что копия плохая. 3.4.2. log - меню доступа к файлам протокола копирования -------------------------------------------------------------------------- Имя : log _________________________________________________________________________ log Меню доступа к файлам протокола копирования

    НАЗНАЧЕНИЕ

Обеспечивает интерфейс в виде меню к файлам протокола, полученным от утилиты autobkp.

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

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

    * ГЛАВА 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 имеются как неотъемлемые общие функции делопроизводства, так и специфические добавки. Данная утилита пытается управлять как основными функциями, так и кругом задач по вашему вкусу. К базовым функциям относятся почта, новости, календарь и база телефонных номе- ров. Добавляемые особенности включают в себя систему напоминаний, основанную на утилите at, и возможность запуска еще одного командного интерпретатора 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, обрам- ленной строками из черточек. Если файл не существует, то выводится сообщение о том, что такого файла новостей нет. Затем вас просят ввести имя другого файла. Этот процесс продолжается в цикл