Перевод "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 (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, включающих одну-две командные строки, которые действуют неожиданно эф- фективно. Набор приложений предоставляет информацию, полезную при програм- мировании с помощью командного процессора и при отладке. Поскольку некоторые из инструментальных средств используют ко- мандные файлы, введенные ранее в данной книге, вы должны работать над книгой в соответствии с последовательностью глав, когда внедряете ко- мандные файлы в вашу систему. Однако вам не помешает сначала пере- листать всю книгу. Введение Многообразие сред системы UNIX Ваш регистрационный каталог: как сделать его комфортным Теории относительности a la 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 / Логические схемы, аппаратные средства \____/
На самом нижнем уровне находятся аппаратные средства и логические цепи. Этот уровень определяет способ хранения и обработки данных во всех аппаратных средствах. Поскольку технология изготовления кремние- вых микросхем продолжает развиваться, этот уровень становится физи- чески меньше и проще, тогда как скорости запоминания и обработки про- должают расти. На этом уровне компонентами являются центральный про- цессор (ЦП), память, микросхемы поддержки и системная шина. Отметим, что хотя прогресс на этом уровне продолжается, это вызы- вает очень малые изменения на верхнем слое пирамиды. Философия системы UNIX состоит в том, чтобы изолировать низкоуровневый аппаратный слой и обеспечить единообразные интерфейсы к нему, которые не нуждаются в из- менениях "наверху". Верхний слой даже не должен знать о нижнем слое. Это не значит, что события в мире аппаратуры не важны в реальном мире, ведь противоречия реального мира влияют на скорость и емкость ресурсов, не говоря уже об их стоимости. Этот уровень во многом похож на язык программирования. Он явля- ется инструментом, который использует архитектор системы для создания "родного" машинного языка. Машинный язык сообщает аппаратуре, какую конкретную команду следует выполнить. В начале эволюции ЦП большинство наборов команд были аппаратно кодированными. Это значит, что когда ЦП получал команду, декодирование и выполнение производилось непосредственно цепями в кремниевой мик- росхеме. Благодаря прогрессу в технологии ЦП, некоторые микросхемы мо- гут быть программируемыми на уровне исполнения команд, что позволяет конструкторам создавать и реализовывать новые наборы команд с мини- мальными усилиями. Данный уровень обеспечивает трансляцию из мнемоник языка ассемб- лера в коды операций и данные машинного языка. Язык ассемблера - это некоторая англо-подобная нотация, которая облегчает человеку понимание и управление работой компьютеров. Условная машина поддерживается ассемблером. Ассемблер может прев- ращать идеи более высокого уровня в цепочки чисел, которые могут быть затем выполнены. Наряду с ассемблером, применяются модели, помогающие использовать аппаратуру компьютера. Здесь мы можем определить такие вещи, как стеки, вектора прерываний и периферийный ввод-вывод. Ядро является следующим логическим продвижением вверх и концепци- ей, которую можно теперь реализовать программно на условной машине. Ядро предоставляет среду, поддерживающую еще большие абстракции, чем те, что рассматривались до сих пор. Двумя наиболее важными абстракция- ми на уровне ядра являются управление процессами для мультипрограмми- рования и многозадачности, и файловая система, которая управляет хра- нением, форматом, поиском файлов и т.п. Когда эти две области перепле- таются, мы имеем базовую функцию многопользовательской машины и ядро операционной системы. Одной из наиболее важных областей, которыми управляет ядро, явля- ется безопасность. Проверки идентификации пользователя выполняются в системных вызовах внутри ядра. Определенные механизмы используются яд- ром для управления безопасностью файлов, устройств, памяти и про- цессов. Единственный способ отключить механизмы безопасности состоит в изменении исходного кода ядра и перекомпиляции всей системы, что крайне нежелательно. Данный уровень строится на ядре, чтобы создать полную операцион- ную среду. Потребность в дополнительных функциях системы можно удов- летворить созданием автономных программ, имеющих конкретное назначе- ние. Таким образом, совокупность всех специфических функций определяет операционную систему. Компилятор - это инструмент (или программа), построенный на опе- рационной системе для дальнейшей разработки более совершенных и более мощных сред. Новые среды могут предполагать еще большие абстракции, чем на нижнем уровне, и делать больше допущений о том, что уже сущест- вует. Это делает возможным символические конструкции более высокого уровня, такие как структуры данных и управляющие структуры. Результа- том является прикладная программа. С помощью компилятора мы можем определить совершенно новый язык и сделать его рабочим на компьютере, написав компилирующую программу, которая читает этот новый язык. Это открывает целые новые области во взаимодействии человека с машиной. Высокоуровневые языки могут вопло- щать различные подходы к решению задач, например, процедурную модель или объектно-ориентированную модель, и в конце концов, очевидно, могут достичь выразительной мощи разговорного языка типа английского. В наше время прикладные программы могут означать массу разнооб- разных вещей. Мы можем предположить, что любая программа, которая сде- лана с помощью компилятора, является прикладной программой. Примерами возможных прикладных программ являются следующее поколение языков, ин- терпретаторов и генераторов прикладных программ. Интерпретатор - это программа, написанная на распространенном языке высокого уровня, кото- рая может декодировать и исполнять другой синтаксис (или язык). Приме- ром, который интересует нас в системе UNIX, является командный про- цессор shell. Это программа на языке Си, созданная для чтения и испол- нения команд, записанных по правилам синтаксиса, определенных команд- ным процессором shell. Генератор прикладных программ - это программа, написанная на язы- ке высокого уровня. Она предназначена для получения достаточной инфор- мации от пользователя о его приложении и может использовать компиля- торный язык, например Си, для написания прикладной программы, реализу- ющей то, что требуется. Пользователь ничего не программирует. Выходом генератора является рабочая программа. UNIX не делает особых различий между уровнями. Некоторые особен- ности системы, например, конвейеры, являются частью ядра на нижнем уровне. Команда типа cat выполняет довольно простую функцию на уровне операционной системы. Нечто подобное ls напоминает простую прикладную программу с относительно малым набором опций. Большие программы, по- добные семейству roff, определенно являются полновесными приложениями, а средства типа sed и awk являются фактически интерпретаторами неболь- ших языков программирования. Замечательной особенностью системы UNIX является единообразие, которое она вносит в этот широкий диапазон функций. Этот верхний уровень является языком, который интерпретирует программа /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, и файлы, которые соз- даются при использовании программ из настоящей книги. Первый файл - это .news_time. Дата этого файла соответствует то- му, когда вы последний раз читали новости в каталоге /usr/news. Для чтения новостей пользуйтесь командой news(1). Эта команда выдает но- вости, появившиеся позже даты создания файла .news_time. Следующий файл - .profile. Этот файл выполняется при каждой ре- гистрации в интерпретаторе shell и может быть использован для привязки вашей собственной среды. В дальнейшем мы рассмотрим этот файл более подробно. Следующий файл - calendar (календарь). Этот файл содержит даты и сообщения. Команда calendar(1) читает в этом файле даты, очень близкие к текущей дате. Затем печатаются или посылаются вам по почте сообще- ния. Последний файл - mbox, ваш системный почтовый ящик. Когда вы с помощью команды mail(1) сохраняете почту, она направляется по умолча- нию в mbox. Первым файлом здесь является .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 Когда вы входите в систему, регистрационная программа выполняет интерпретатор 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, обрам- ленной строками из черточек. Если файл не существует, то выводится сообщение о том, что такого файла новостей нет. Затем вас просят ввести имя другого файла. Этот процесс продолжается в цикле, пока вы не введете только возврат каретки. Когда вы выходите из цикла в строке 62, выполняется команда cd для возврата в тот каталог, откуда вы нача- ли работать. Эта команда cd не оказывает никакого влияния после за- пуска утилиты office. Вы должны указать команду cd вашему регистраци- онному shell. Никакая другая программа, запущенная из порожденного shell, не может выполнить для вас команду cd. Строки 64-77 выполняют функции календаря. Сначала выводятся две пустые строки. Почему две? Потому что обычно команда echo вставляет символ новой строки в конце каждой выводимой строки, а выводимой в данный момент строкой является еще один символ новой строки. Затем в строке 65 запускается программа today. Она печатает изображение кален- даря на месяц. Строка 66 проверяет, имеется ли календарный файл в ва- шем регистрационном каталоге. Если да, он выводится в длинном формате команды ls в строке 67. Печатается также сообщение-заголовок "Calendar Notifications" (календарные извещения). Строка 69 запоминает текущий рабочий каталог переменной PWD. За- тем мы переходим командой cd в каталог $HOME, чтобы находиться в том же каталоге, что и календарный файл. В строке 71 вызывается команда UNIX calendar. Она просматривает календарный файл и выводит все подхо- дящие по времени сообщения. По команде cd мы возвращаемся в наш исход- ный каталог для того, чтобы восстановить порядок. Строка 73 выводит сообщение, чтобы вы проверили вашу почту на наличие календарных за- писей. Как указывалось ранее, команда calendar работает двумя способа- ми. Оператор else в строке 74 выводит вам сообщение, если у вас нет календарного файла. По завершении оператора if, в строках 76 и 77 пе- чатается сообщение о том, что нужно нажать возврат каретки, и выполня- ется оператор read. Это блокирующее чтение, которое означает остановку и ожидание какого-либо ввода. Это противоположность неблокирующему чтению, или более широко известной технике чтения с опросом. Опрос возможен в программах, написанных на Си, но запуск опроса в shell мо- жет потребовать немного больших усилий. Для изучения чтения с опросом ознакомьтесь с главой, описывающей терминальные устройства. Строка 78 управляет командой phone. Она вызывает утилиту phone, которую мы рассмотрели ранее, после чего управление передается в конец цикла while. Печатается главное меню, и все начинается сначала. Строки 79-97 управляют автоматическими напоминаниями. Сначала за- пускается программа greet. Она была описана ранее в этой главе. Затем в строках 80 и 81 в режиме фонового выполнения вызываются два команд- ных файла at, которые выполнятся позже в течение дня. В строке 84 вам задается вопрос, желаете ли вы оставить какие-то свои напоминания. Если вы отвечаете "y", выполняются строки 87-96. Если вы не ввели "y", оператор else не выполняется, поэтому управление передается главному меню. Вы видите, что эта возможность предполагает однократное исполь- зование в течение дня. Строки 87-89 выводят некоторую информацию, необходимую для того, чтобы вы завели для себя напоминания. Выводится синтаксис программы at, текущая дата (только время) и затем приглашение вводить вашу ко- манду. Строка 90 читает вашу команду. Строка 91 означает, что если был введен не только возврат каретки, то нужно выполнить этот цикл. Поэто- му цикл while продолжается, пока вы не нажмете только возврат каретки. Строка 93 пытается выполнить команду, которую вы ввели. Команда eval используется для того, чтобы раскрыть значения тех переменных, которые могли быть не раскрыты ранее. Строка 94 просит ввести следующую коман- ду, а строка 95 читает ее. Поскольку чтение стоит в самом конце цикла, оператор while выполняет проверку нового ввода с клавиатуры. Строки 98-102 управляют выходом в shell. Эта часть программы поч- ти идентична циклу, использованному для напоминаний. Нам выдается приглашение на ввод команды, она зачитывается в переменную, затем де- лается синтаксический разбор этой переменной с помощью команды eval. Если вы хотите перейти в shell, выполните команду sh - и она породит shell. Строки 101 и 102 не дают появиться на экране главному меню до тех пор, пока вы не нажмете возврат каретки. Строки 103-105 выполняют проверку на ошибки ввода. Любая непра- вильная команда попадает сюда. Выводится сообщение об ошибке и выда- ется приглашение "hit " (нажмите возврат каретки).

    * ГЛАВА 6. УПРАВЛЕНИЕ ЛИЧНОЙ ИНФОРМАЦИЕЙ II: *

    ОСВЕДОМЛЕННОСТЬ ПОЛЬЗОВАТЕЛЯ

    И ЗАЩИТА ЛИЧНОЙ ИНФОРМАЦИИ

    ОСВЕДОМЛЕННОСТЬ ПОЛЬЗОВАТЕЛЯ

activ показать активность терминалов info показать информацию о паролях пользователей uchk посмотреть процессы других пользователей watch наблюдать за регистрацией в системе whox команда who с дополнительными возможностями

    ЗАЩИТА ЛИЧНОЙ ИНФОРМАЦИИ

acme показать учетную информацию inuse запретить использование терминала lock блокировать и разблокировать файлы УПРАВЛЕНИЕ ЛИЧНОЙ ИНФОРМАЦИЕЙ II:

    ОСВЕДОМЛЕННОСТЬ ПОЛЬЗОВАТЕЛЯ

    И ЗАЩИТА ЛИЧНОЙ ИНФОРМАЦИИ

    ВВЕДЕНИЕ

Мы уже разработали целый ряд инструментальных средств, которые помогают нам более эффективно управлять нашей личной информацией. Од- нако мы работаем не в вакууме. Каждый из нас работает с другими людьми и обычно использует систему вместе с другими пользователями. По нескольким причинам важно иметь представление о том, что делают в системе другие люди. Во-первых, у нас может возникнуть необходимость наметить работу, интенсивно использующую процессор, на то время, когда загрузка системы невелика. Изучение типичных вариантов загрузки систе- мы может помочь нам спланировать такую работу и сотрудничать с другими пользователями со взаимной выгодой. Нам также может понадобиться знать, находится ли кто-то на своем рабочем месте, чтобы мы могли поговорить с ним, или знать, что кто-то очень занят и был бы недоволен, если его прервут. Наконец, существуют вопросы защиты информации, которым мы уделим больше внимания в последующих главах. Основой защиты информации явля- ется осведомленность о том, что делают другие - и, возможно, о том, что они не должны делать. Поэтому мы здесь представим две группы инструментальных средств. Средства ОСВЕДОМЛЕННОСТИ ПОЛЬЗОВАТЕЛЯ помогают нам следить за тем, что делают другие, и тем самым упростить связь с ними. Средства ЛИЧНОЙ ЗА- ЩИТЫ важны для защиты нашей учетной информации и наших данных и для получения информации о наших собственных действиях. Первая группа рассматриваемых здесь средств, относится к "осве- домленности пользователя". Этими средствами являются activ, info, uchk, watch и whox. Командный файл activ представляет собой модификацию команды who, показывающую имя каждого пользователя, номер терминала и сведения о том, насколько давно данное лицо вводило что= либо с клавиатуры. По этой информации мы можем сказать, кто еще работает в системе, активно ли они работают и, если они используют закрепленные линии, то где они находятся. Следующее средство - info. Оно предоставляет возможность получить информацию о паролях всех указанных в списке пользователей. С помощью этой утилиты вы можете получить общую информацию об этих пользователях и увидеть, где размещаются их регистрационные каталоги. Еще одно информативное средство - uchk. Этот командный файл поле- зен для выяснения того, что делает указанный пользователь в настоящее время. По этой информации вы можете сказать, простаивают ли они или же делают что-то важное и их нельзя прерывать. Следующее средство - watch. Эта программа похожа на "демон", ко- торый выполняется в фоновом режиме и постоянно следит за регистрацией определенных пользователей. Когда нужное вам лицо регистрируется, на ваш терминал посылается сообщение о том, что данный пользователь вошел в систему. Таким образом, вы можете продолжать работу вместо того, чтобы постоянно прерывать ее. Последняя утилита в данном разделе - это whox. Whox дает вам исчерпывающие сведения обо всех пользователях, зарегистрировавшихся на машине. Вы можете получить информацию, похожую на результат команды who, которая отсортирована различным образом, отобразить информацию о терминалах или даже передать почтовое сообщение любому пользователю.

    ОСВЕДОМЛЕННОСТЬ ПОЛЬЗОВАТЕЛЯ


ИМЯ: activ
activ Показывает активность терминалов

    НАЗНАЧЕНИЕ

Показывает имена всех зарегистрированных пользователей, номера их терминалов и время, когда они последний раз работали с клавиатурой.

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

activ

    ПРИМЕР ВЫЗОВА

activ Выводит на экран информацию об активности пользователей на своих терминалах

    ТЕКСТ ПРОГРАММЫ

1 : 2 # @(#) activ v1.0 Display terminal activity Author: Russ Sage 2а Показать активность терминалов 4 if [ $# -gt 0 ] 5 then echo "activ: argument error" >&2 6 echo "usage: activ" >&2 7 exit 1 8 fi 10 who -u | cut -c1-17,38-42 ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ activ? Первоначально компьютеры использовались исключительно для запуска замкнутых, независимых задач обработки информации. Появление много- пользовательских систем сделало возможным по крайней мере элементарное общение пользователей между собой. Такое общение в настоящее время яв- ляется аспектом возрастающей важности при работе с компьютером. Система UNIX предоставляет команды mail и write для пакетного и интерактивного диалога соответственно. С помощью телефона мы не можем сказать, доступен ли кто-либо для общения с нами, пока не позвоним и не поговорим, возможно, с одним или несколькими другими людьми. Поскольку система UNIX постоянно записывает информацию о деятельности пользователей, было бы полезным уметь использовать эту информацию, чтобы узнать, кто доступен для общения, и получить общее представление о том, чем они занимаются. После этого мы можем решать, как и когда мы хотим связаться с ними. Activ может рассказать нам о том, сколько времени прошло с тех пор, когда кто-либо нажимал клавиши на своей клавиатуре. Если в вашей машине есть выделенные линии, то вы также можете узнать, находится ли данное лицо на своем рабочем месте. ЧТО ДЕЛАЕТ activ? Activ печатает информацию об активности всех пользователей систе- мы. Распечатка содержит имя пользователя, номер терминала, на котором он зарегистрировался, и время последнего обращения к клавиатуре. Если клавиатура была использована в течение последней минуты, этот терминал отмечается точкой. Если терминал не был использован в течение послед- них 24 часов, напротив него печатается слово "old". Во всех остальных случаях выводится время последнего использования терминала в минутах и секундах. Вот пример такой распечатки:
| | root tty01 01:23 | sage tty05 . | batch tty12 old | nuucp tty16 . | Важным вопросом является то, как система узнает, когда кто-либо работает с клавиатурой? Поскольку терминальные устройства являются файлами, мы можем собрать информацию о терминале путем просмотра состояния файла, связанного с ним. В данном случае искомая информация - время последнего использования клавиатуры. То, что мы ищем, уже есть в системе. Это опция -u команды who. Эта опция, однако, существует только в системе AT&T UNIX System V, по- этому команда activ, по крайней мере в представленном виде, может быть реализована только в этой системе. Activ берет выход команды "who -u" и "отрезает" некоторые данные, не имеющие отношения к нашей цели. Путем проб и ошибок, а затем проверки исходного кода я обнаружил, что значение, используемое в команде "who -u", является временем "мо- дификации" файла устройства, которое сообщает системный вызов stat(2). На страницах руководства по stat дается полная таблица системных вызо- вов, которые изменяют временные характеристики файлов. В табл. 6.1 представлены эти характеристики: Таблица 6.1 Временные характеристики файла
Временные Системные вызовы, изменяющие временные характеристики характеристики
Время доступа creat, mknod, pipe, utime, read Время модификации creat, mknod, pipe, utime, write Время создания creat, mknod, pipe, utime, write, chmod, chown, link
Давайте на некоторое время отвлечемся и поговорим о том, что эти временные характеристики означают и как они могут быть модифицированы. Одно интересное замечание: UNIX дает вам как обычному пользователю возможность установить любое время доступа и время модификации файла по вашему желанию. Это и хорошо, и плохо. Это обеспечивает гибкость, когда дело касается информации индексного дескриптора файла, но также позволяет скрывать следы при нарушении защищенной информации. Возьмем, например, файл login. Кто-нибудь мог бы вставить новую строку регист- рации перед старой и затем вернуть время доступа и время модификации в исходные значения. Тогда бы никто, просматривая файл /bin/login коман- дой ls, не смог бы сказать, что файл был изменен. Однако в системе UNIX имеется третье время - время создания. Система не позволяет вам изменить это время при помощи обычных команд. Системный вызов utime(2) обеспечивает изменение только времени доступа и модификации. Команда touch(1) также может изменить время доступа и модификации, но не время создания. Команда touch строится только на системном вызове. Она может делать не больше того, что обеспечивает системный вызов. Команда fsdb(1) (отладчик файловой системы) является единственным способом преодоления защиты от изменения времени созда- ния. Но даже fsdb не может управлять временем создания. Вы должны вый- ти за пределы поддерживаемых fsdb требований безопасности. Как это сделать, показано в последующих главах. В случае изменения регистрационной записи, рассмотренном выше, вы могли бы посмотреть на время создания и увидеть, что оно очень близко к текущему времени. Если же кто-либо использовал fsdb для подделки времени создания, вы никогда не будете иметь уверенности в том, что файл не был изменен. Какое же все это имеет отношение к определению того, делал ли пользователь что-либо недавно за своим терминалом? Время модификации изменяется вызовом write(2). Поэтому представляется разумным, что за- пись на терминал будет иметь место, когда драйвер читает символ, а за- тем посылает его обратно на экран. По мере того, как кто-то нажимает на клавиши, время модификации постоянно обновляется при эхо-отображе- нии символов. Когда символы перестают поступать с клавиатуры, приоста- навливается запись в файл терминала. Формула для определения последне- го времени работы за терминалом (last_activity) такова: last_activity = time(NULL) - mod_time, где mod_time - время модификации. Эта формула закодирована внутри ко- манды who(1). Отметим, что вызов команды time со значением NULL возв- ращает истинное текущее время. Команда activ не имеет опций. Если вы используете опции, напеча- тается сообщение об ошибке.

    ПОЯСНЕНИЯ

В строках 4-8 выполняется проверка на наличие ошибок. Если число аргументов командной строки больше нуля, на стандартное устройство ре- гистрации ошибок выводится сообщение и программа завершается с неудач- ным статусом. Строка 10 - это команда, выполняющая вывод. Команда who вызвана с опцией -u для получения основных данных. Затем ее выход по конвейеру передается команде cut, которая отображает колонки 1-17 и 38-42. Тем самым печатается только три поля, как показано в нашем предыдущем при- мере.
ИМЯ: info
info Вывод на экран информации о пароле пользователя

    НАЗНАЧЕНИЕ

Печатает информацию поля комментария из файла /etc/ passwd для указанного пользователя.

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

info login_name [ login_name ... ]

    ПРИМЕР ВЫЗОВА

info russ Печатает информацию, которая хранится о пользователе russ

    ТЕКСТ ПРОГРАММЫ

1 : 2 # @(#) info v1.0 Display password info on a user Author: Russ Sage 2а Отобразить парольную информацию пользователя 4 for NAME in $@ 5 do 6 USER=`grep "^${NAME}:" /etc/passwd` 7 echo "$NAME:\t`echo ${USER}|cut -d: -f6`\t` echo ${USER}|cut -d: -f5`" 8 done

    ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ

NAME Каждое имя, указанное в командной строке USER Полная запись в файле /etc/passwd для данного имени ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ info? Система UNIX использует конфигурационные файлы для хранения основной информации о пользователях и другой системной информации. Од- ними из наиболее популярных конфигурационных файлов являются /etc/group, /etc/passwd и /etc/inittab. Для получения информации о пользователях нам необходимо заглянуть в эти файлы. В системе Berkeley имеется команда finger, которая получает ин- формацию о пользователях из их регистрационных каталогов и из файла паролей. В System V не так много программ, выполняющих такого рода ра- боту, поэтому нам необходимо разработать их. ЧТО ДЕЛАЕТ info? Info - это командный файл, который получает информацию из регист- рационного каталога и комментарий о пользователе из файла /etc/passwd. Результат выглядит так:
| | name: home dir comments | имя: регистрационный каталог комментарии | Если в вашей системе используется это поле комментариев, то эта информация может быть полезной. "Вручную" выполнить просмотр пользова- телей в файле паролей можно следующим образом: grep login_name /etc/passwd При этом распечатывается вся строка со всеми полями данных. Info берет эти необработанные данные и выделяет из них каталог регистрации пользователя и поле комментария. В командной строке можно указать несколько регистрационных имен. Каждое имя берется по порядку из командной строки.

    ПРИМЕР

$ for NAME in `cat /etc/passwd | cut -d: -f1` > do > NAMELIST="$NAMELIST $NAME" > done; info $NAMELIST Имя каждого пользователя в файле паролей добавляется к списку имен. Затем этот список передается в командную строку info, которая печатает все данные. Смысл команды: "Дай мне информацию обо всех поль- зователях системы".

    ПОЯСНЕНИЯ

Строки 4-8 - это цикл for, который обрабатывает все имена, пере- данные в командной строке. Для каждого переданного имени выполняются строки 6 и 7. В строке 6 в переменную USER заносится результат команды grep, заключенной между символами ударения (`). Начиная с начала строки (обозначено символом ^), grep ищет имя, за которым следует символ дво- еточия (:). Такое указание заставляет выполнять поиск образца только в первом поле файла паролей. В строке 7 мы отображаем нашу выходную строку командой echo, внутри которой вложены другие команды echo. Мы могли бы получать эле- менты информации отдельно, присваивая их значения отдельным перемен- ным, и затем создавать необходимый формат вывода, используя значения этих переменных. Однако помещение всей информации в командную строку работает быстрее, и текст программы более компактный, хотя и не очень читабельный. Мы можем также использовать символы форматирования в ко- манде echo для форматирования нашей распечатки. Сначала отображается имя в том же виде, как оно получено из ко- мандной строки. Затем выводится табуляция (\t). За первой табуляцией следует поле номер шесть из файла паролей. Поскольку мы еще не имеем этих данных, мы должны выделить их из значения переменной USER, кото- рую мы уже имеем из предыдущей строки. Чтобы сделать это, мы командой echo выводим всю строку и выделяем шестое поле, используя разделяющие двоеточия. После этого поля мы выводим еще одну табуляцию и затем пя- тое поле файла паролей. Мы получили это поле таким же образом, как и шестое поле - эхо-отображением и выделением. Такая техника получения данных медленная, поскольку вовлечены все процессы, но это самый быстрый путь сделать это на языке shell. Коман- да awk была бы понятнее и, возможно, быстрее, но наша реализация де- монстрирует гибкость языка shell. Язык shell может выполнять почти все, но не всегда лучшим образом. Вот почему в некоторых случаях мы используем язык Си, в чем вы убедитесь по мере продвижения по нашей книге.
ИМЯ: uchk
uchk Проверка процессов, запущенных другим пользователем

    НАЗНАЧЕНИЕ

Отобразить все процессы каждого пользователя, указанного в ко- мандной строке

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

uchk [-a] login_name [...]

    ПРИМЕР ВЫЗОВА

uchk -a Вывод всех процессов, запущенных администраторами

    ТЕКСТ ПРОГРАММЫ

1 : 2 # @(#) uchk v1.0 Check processes of another user Author: Russ Sage 2а Проверка процессов другого пользователя 4 trap "rm /tmp/ps$$ 2> /dev/null" 0 1 2 3 15 6 if [ $# -eq 0 ] 7 then echo "uchk: argument error" >&2 8 echo "usage: uchk [-a] login_name [ ... ]" >&2 9 exit 1 10 fi 12 if [ "`echo $1 | cut -c1`" = "-" -a "$1" != "-a" ] 13 then echo "uchk: invalid argument $1" >&2 14 echo "usage: uchk [-a] login_name [ ... ]" >&2 15 exit 1 16 fi 18 ADMIN="administrators names go here" 19 if [ "$1" = "-a" ] 20 then shift 21 set $ADMIN $@ 22 fi 24 ps -ef > /tmp/ps$$ 25 for NAME 26 do 27 echo 28 fgrep "$NAME" /tmp/ps$$ 29 done

    ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ

ADMIN Строка с именами пользователей, являющихся администраторами в вашей системе NAME Содержит каждое имя, считываемое из командной строки ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ uchk? Поскольку UNIX является многопользовательской системой, множество задач выполняются одновременно. Единственный способ следить за такими задачами - с помощью команды ps. Ps - это довольно специфичная команда в том смысле, что она должна обращаться к памяти (/dev/mem) и прохо- дить по связанному списку структур процесса. Это тяжелая вещь, поэтому нелегко сделать свою собственную команду для вывода такого рода инфор- мации. Вдобавок память защищена, и доступ к ней имеет только пользова- тель root. Так что мы должны или удовлетвориться опциями, которые дает ко- манда ps, или модифицировать вывод этой команды. Uchk скорее относится к последней категории. Мне хотелось получить список состояний про- цессов по имени пользователя, а не смесь информации, относящейся к разным пользователям. Хотя то, что я хотел, не соответствует ни одной из стандартных опций команды ps, я смог модифицировать ее выход для получения того, что мне надо. ЧТО ДЕЛАЕТ uchk? Uchk - это средство, которое генерирует, анализирует и сообщает о процессах, запущенных каждым указанным пользователем. За один раз вы можете проверить столько пользователей, сколько вам нужно. Все про- цессы для каждого пользователя печатаются вместе. Чтобы делать это, uchk должен работать с временными файлами. Система UNIX весьма элегантно управляет временными файлами. Shell поз- воляет использовать идентификатор процесса для создания уникальных имен файлов для каждого запуска. После того как утилита запущена, вре- менные файлы очищаются путем использования команды trap в shell'е. Ко- манда trap удаляет файл, если что-либо прервало выполнение командного файла или когда программа завершается. Это прекрасная особенность, ко- торая исключает накопление случайных файлов в системе. Если uchk была вызвана без аргументов или была использована недо- пустимая опция, то печатается сообщение об ошибке и выполнение завер- шается. Uchk имеет переменную ADMIN, которая определяет всех администра- торов вашей системы. Отредактируйте символьную строку, присваиваемую переменной ADMIN, указав имена администраторов, которых вы хотите про- верить. Имена должны быть разделены пробелами. Это позволит вам прове- рять процессы ваших администраторов, указывая опцию -a в командной строке. Все остальные имена должны быть указаны в командной строке. Естественно, вы таким же образом могли бы установить другие группы пользователей. Слежение за администраторами поможет вам оценить, как они управляют сохранностью информации, а также распознать администра- тивные задачи, которые имеют склонность загружать процессор.

    ПРИМЕР

$ uchk -a russ uucp Показывает процессы всех администраторов, мои собственные, и про- цессы uucp по порядку. Все сообщения об ошибках выводятся на стандарт- ное устройство регистрации ошибок, а списки процессов выводятся на стандартное устройство вывода.

    ПОЯСНЕНИЯ

Строка 4 - это оператор trap. Символьная строка между двойными кавычками содержит команды, которые должны быть выполнены, когда про- исходит прерывание. В этом случае мы удаляем временный файл и переад- ресовываем все выходные данные на нулевое устройство. Когда команда rm пытается удалить несуществующий файл, выводятся сообщения об ошибках. Поскольку мы не знаем, какие файлы могут быть в наличии в тот момент, когда возникнет прерывание, то мы хотим избавиться от сообщений об ошибках. Оператор trap активизируется по выходу из программы (program exit, сигнал 0), разрыву линии (hangup, сигнал 1), прерыванию (interrupt, сигнал 2), выходу (quit, сигнал 3) или программному завер- шению (software termination, сигнал 15). В строках 6-10 проверяется, переданы ли какие-либо аргументы. Если вы вызвали uchk без аргументов, выводится сообщение об ошибке и uchk завершается. В строках 12-16 проверяется, указана ли какая-то опция со знаком минус и является ли она опцией -a - единственно допустимой опцией. Ко- мандный файл делает это, применяя команды проверки для сравнения двух различных случаев. Первая проверка вырезает первый символ первого по- зиционного параметра и смотрит, является ли он символом "-". Следующая проверка делается для того, чтобы увидеть, что первый позиционный па- раметр не является опцией -a. Поскольку обе проверки соединены опера- цией AND, то для получения значения "истина" они обе должны быть истинными. Если они обе истинны, выводится сообщение об ошибке и uchk завершается. Почему мы должны выполнять такую сложную проверку? Проблема в том, что у нас нет иного способа определить, является первый позицион- ный параметр опцией или нет. Он может быть опцией, а может быть име- нем, которое нужно искать. Таким образом, мы должны задать вопрос: "Является ли это опцией, и если да, то допустима ли эта опция?" В строке 18 переменная ADMIN инициализируется символьной строкой, следующей за ней. Это строка, которую вы должны модифицировать в соот- ветствии с вашей системой. Все, что вы должны сделать - это использо- вать редактор vi и вставить в эту строку имена администраторов, разде- ленные пробелами. Ниже мы используем переменную ADMIN для обработки аргументов командной строки. В строках 19-22 проверяется, была ли указана в командной строке опция -a. Если да, эта опция удаляется из командной строки с целью из- бавления от нее. Строка 21 использует команду set для того, чтобы по- местить символьную строку ADMIN в позиционные параметры. Команда set вставляет значение переменной ADMIN, начиная с первого позиционного параметра, и сдвигает все настоящие параметры вправо. Тем самым в цикл for передается множество имен, которые должны быть обработаны. В строке 24 выполняется команда ps для всех пользователей. Эта команда использует опцию f для вывода большого количества данных. Ре- зультат помещается во временный файл, в имени которого применяется идентификационный номер процесса. Этот один большой файл представляет собой источник для остальных распечаток. Возможно, все это немного отстает от реального времени, но программа выполняется гораздо быст- рее, чем при многократном вызове команды ps. Строки 25-29 представляют собой цикл for, который выполняется от $1 до $x, где x - последний позиционный параметр. Для каждого имени выполняется следующее: печатается пустая строка для разделения листин- га (строка 27), затем командой fgreps (для ускорения) выбираются из временного файла все процессы, принадлежащие данному пользователю. Благодаря применению команды fgrep для каждого имени пользователя, все процессы данного пользователя печатаются за один раз. Когда закончится проверка всех имен, указанных в командной строке, цикл завершится, за- вершится командный файл и сработает оператор trap, который удаляет вре- менный файл.
ИМЯ: watch
watch Наблюдение за регистрацией указанных пользователей

    НАЗНАЧЕНИЕ

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

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

watch [-k] [login_name ...]

    ПРИМЕР ВЫЗОВА

watch Наблюдение за регистрацией всех пользователей, указанных во внутренней переменной LIST

    ТЕКСТ ПРОГРАММЫ

1 : 2 # @(#) watch v1.0 Watch for specific logins Author: Russ Sage 2а Наблюдение за регистрацией пользователей 4 if [ "`echo $1 | cut -c1`" = "=" -a "$1" != "-k" ] 5 then echo "watch: invalid argument $1" >&2 6 echo "usage: watch [-k] [login_name ...]" >&2 7 echo " -k kill background process" 8 exit 1 9 fi 11 if [ "$1" = "-k" ] 12 then if [ -s $HOME/.watch ] 13 then echo "killed `cat $HOME/.watch`" 14 kill `cat $HOME/.watch` 15 rm $HOME/.watch 16 exit 0 17 fi 18 fi 20 echo $$ > $HOME/.watch 22 LIST="root sys bin administrator1 administrator2 $*" 24 while : 25 do 26 for NAME in `who | cut -d" " -f1` 27 do 28 for PERSON in $LIST 29 do 30 if [ "$NAME" = $PERSON" ] 31 then echo ONLINE: $NAME 32 fi 33 done 34 done 35 sleep 10 36 done &

    ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ

HOME Полный маршрут к вашему регистрационному каталогу LIST Список имен пользователей системы, разделенных пробелами NAME Содержит имена зарегистрированных в настоящий момент пользователей PERSON Отдельное имя из списка имен в переменной LIST ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ watch? В течение рабочего дня множество людей входят в систему и выходят из нее. Иногда единственный способ связаться с человеком - через маши- ну. Нам нужно средство, которое автоматически сообщит нам, что нужное лицо на связи. ЧТО ДЕЛАЕТ watch? Подразумевается, что watch является фоновой задачей, которая постоянно следит за тем, кто зарегистрировался. Когда лицо или лица, предварительно вами отмеченные, регистрируются в системе, на ваш экран выводится сообщение о том, что они на связи. Количество имен, за которыми вы можете следить, не ограничено. Общий перечень имен - это объединение имен, указанных в командной строке, и списка системных пользователей, т.е. пользователя root и всех сопровождающих его администраторов. В командном файле watch список системных имен ВСЕГДА включен. Это вызвано тем, что это важные пользователи и вы хотите всегда знать об их входе в систему и выходе из нее. Это отличает watch от uchk, поскольку последний требует указа- ния опции -a для включения списка с системными именами. Хотя мы устанавливаем watch с точки зрения пользователя, который желает следить за администраторами и другими пользователями, админист- раторы тоже могут иметь список "критических пользователей" или предпо- лагаемых нарушителей защиты информации и использовать watch для полу- чения сигнала о регистрации таких пользователей. За кем бы мы ни следили, мы сталкиваемся с одной проблемой. После регистрации в системе указанного лица сообщение об этом поступает на ваш экран, не обращая внимания на то, чем вы в данное время занима- лись, что не очень приятно. Единственный способ остановить вывод сооб- щения - аварийно завершить watch командой kill. Это легко сделать пу- тем помещения идентификатора этого процесса в файл $HOME/.watch. Затем этот номер может быть использован в операторе kill для остановки вы- полнения командного файла. Для того чтобы попроще избавиться от watch, возможность аварийного завершения оформлена в виде опции -k данного командного файла.

    ПРИМЕРЫ

1. $ LIST="root bin" watch daemon Если переменная LIST не была инициализирована внутри самого ко- мандного файла watch, то мы можем инициализировать ее на командном уровне shell'а, а затем вызвать watch. Список пользователей для watch будет такой: root, bin и daemon по порядку, поскольку watch добавляет имена, указанные в ее командной строке к именам, имеющимся в перемен- ной LIST. Такой способ указания имен более гибок, чем жесткое програм- мирование части списка в тексте командного файла watch, но он требует помнить больше информации и больше нужно набирать на клавиатуре. 2. echo "Watch (y/n): \c" read ANS if [ "$ANS" = "y" ] then watch fi Это фрагмент, который вы можете вставить в ваш .profile. Когда вы регистрируетесь, вам автоматически предлагается запустить watch. Если вы ответите "y", watch станет фоновой задачей и запустится по умолча- нию (просматривая пользователей согласно списку в переменной LIST). Если будет введен любой символ, отличный от y, watch не запустится.

    ПОЯСНЕНИЯ

Строки 4-9 выполняют проверку на наличие ошибок в опциях команд- ной строки. Для первого позиционного параметра проверяется, что он имеет тире и не является единственной допустимой опцией "-k". Если ре- зультатом проверки является истина, печатается сообщение об ошибке и командный файл завершается. Строки 11-16 проверяют, что первый позиционный параметр - это -k. Если это так, значит пользователь хочет уничтожить уже запущенный про- цесс watch. В этом случае выводится сообщение, указывающее идентифика- тор процесса, который будет уничтожен, и выполнение продолжается. В строке 12 мы смотрим, существует ли в нашем регистрационном каталоге файл с именем .watch. Если нет, то это означает, что предыдущий эк- земпляр watch предположительно уже уничтожен и нет необходимости пы- таться сделать это снова, поэтому происходит переход к оставшейся части программы и выполнение watch происходит так, как будто опция "-k" не была указана. Если же файл .watch имеется, то в строке 14 используется команда kill для уничтожения фонового процесса watch. Напомним, что при использовании опции -k мы подразумеваем, что watch был вызван ранее, поэтому файл $HOME/.watch имеет идентификационный номер самого про- цесса watch. В строке 15 удаляется временный файл watch и в строке 16 происходит выход из программы. Теперь командный файл watch более не выполняется как фоновый. Строка 20 выполняется, если опция -k не была указана или если нет файла .watch. (Последнее может произойти, если пользователь пытается уничтожить процесс, забыв, что он уже был уничтожен.) Если опция -k не была указана, мы можем считать, что watch был вызван, чтобы стать фо- новым процессом и выполнять свою работу. Для того чтобы сделать это, текущий процесс отображает свой идентификатор процесса в файл .watch. Этот файл остается в вашем регистрационном каталоге до тех пор, пока он не будет удален вручную или же изменен путем повторного запуска watch. В строке 22 инициализируется переменная LIST. Ее значением явля- ется символьная строка с именами, разделенными пробелами. Вам нужно вручную отредактировать переменную LIST перед запуском в вашей систе- ме. Просто уберите ее нынешнее содержимое и вставьте туда имена адми- нистраторов вашей системы. Если в командной строке будут указаны до- полнительные имена пользователей, они будут добавлены в переменную LIST посредством символов расширения параметров $*. Тем самым перемен- ная LIST станет основным списком всех имен пользователей, за которыми будет вестись наблюдение. Строки 24-36 выполняют цикл постоянного наблюдения. В начале каж- дой итерации с помощью команды who создается список имен пользовате- лей, который передается циклу for в строке 26. Цикл for использует ко- мандную подстановку для получения списка слов, образованного из перво- го поля команды who. Каждое зарегистрированное имя сравнивается со списком предварительно определенных имен, за которыми мы наблюдаем. Обратите внимание, что внешний цикл while сам себя помещает на выпол- нение в фоновом режиме. Это означает, что вам нет необходимости вво- дить это с клавиатуры. Строки 29-33 управляют внутренним циклом, который проходит по именам, содержащимся в нашем основном списке, и сравнивает их с имена- ми, полученными от команды who. Когда имя, полученное от команды who (имя зарегистрированного пользователя) совпадает с именем в нашем списке, на экран выводится сообщение о том, что данное лицо зарегист- рировалось. После того как все имена проверены, командный файл watch приоста- навливается на 10 секунд (строка 35). Когда он снова пробуждается, вы- полняется следующая итерация вечного цикла while. Все зарегистрирован- ные имена вновь сравниваются со списком. Это будет продолжаться до тех пор, пока вы не прекратите выполнение watch. Как отмечалось ранее, watch можно легко уничтожить с помощью опции -k или же вручную путем ввода команды "kill `cat $HOME/.watch`".

    МОДИФИКАЦИИ

Watch выполняет довольно мало работы и использует какую-то часть времени центрального процессора. Вы можете поэкспериментировать с уве- личением интервала паузы (sleep), чтобы watch запускался не так часто. Большинство пользователей находятся в системе по крайней мере минуту, поэтому вы можете попробовать значение sleep(60). Вы по-прежнему може- те обнаружить регистрацию всех интересующих вас пользователей?
ИМЯ: whox
whox Команда who с дополнительными возможностями

    НАЗНАЧЕНИЕ

Предоставляет много дополнений к выходу команды who и позволяет применять данные who для других приложений.

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

whox [-f] [-n] [-m] [-p] [-t] [-w] [-x] где -f указывает каждого зарегистрированного пользователя -n сортирует выход команды who по именам -m передает почту каждому пользователю -p выводит информацию о паролях пользователей -t сортирует выход команды who по времени (умолчание) -w показывает возможность записи на зарегистрированные терминальные устройства -x дополнительная информация о регистрационном каталоге и паролях

    ПРИМЕР ВЫЗОВА

whox -w Показывает права доступа к файлу (возможность чтения и записи) для каждого зарегистрированного терминального устройства

    ТЕКСТ ПРОГРАММЫ

1 : 2 # @(#) whox v1.0 Who with expanded options Author: Russ Sage 2а Команда who с дополнительными опциями 4 XTRA="no" 5 SORT="sort -b +2" 6 DISPLAY="norm" 8 CUT1="cut -d' ' -f1" 9 CUT5="cut -d: -f5" 10 CUT6="cut -d: -f6" 12 for ARG in $@ 13 do 14 case $ARG in 15 -f) DISPLAY="finger" 16 COMMAND="finger \$NAME; echo";; 17 -n) SORT="sort";; 18 -m) DISPLAY="mail";; 19 -p) DISPLAY="pass" 20 COMMAND="grep \"^\$NAME:\" /etc/passwd";; 21 -t) SORT="sort -b +2";; 22 -w) DISPLAY="write";; 23 -x) XTRA="yes";; 24 *) echo "whox: invalid option $ARG" 25 echo "usage: whox [-f] [-n] [-m] [-p] [-t] [-w] [-x]" 26 echo " -f finger users" 27 echo " -n sort by name" 28 echo " -m mail to each user" 29 echo " -p password info on users" 30 echo " -t sort by time (default)" 31 echo " -w show writeability of devices" 32 echo " -x extra home dir and gcos info" 33 exit 1;; 34 esac 35 done 37 if [ "$XTRA" = "yes" ] 38 then EXTRA="| while read LINE; do \ 39 NAME=\`echo \$LINE | cut -d' ' -f1\`;\ 40 ENTRY=\`grep \"^\$NAME:\" /etc/passwd\`;\ 41 echo \"\$LINE\t\`echo \$ENTRY|\$CUT6\`\t\`echo \$ENTRY|\$CUT5\` \";done" 42 else EXTRA="" 43 fi 45 case $DISPLAY in 46 norm) eval "who | $SORT $EXTRA";; 47 finger|pass) for NAME in `who | $SORT | cut -d' ' -f1` 48 do 49 eval $COMMAND 50 done;; 51 mail) who | cut -d' ' -f1 | while read NAME 52 do 53 echo "mail to $NAME (y/n): \c" 54 KB=`line < /dev/tty` 55 if [ "$KB" = "y" ] 56 then mail $NAME < /dev/tty 57 fi 58 done;; 59 write) ls -il `who | sed "s/...........\(.......\).* /\/dev\/\1/"`;; 60 esac

    ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ

ARG Аргументы командной строки COMMAND Команда, которую следует выполнить при использовании команды who со списком имен CUT1 Содержит синтаксис для выделения первого поля строки CUT5 Содержит синтаксис для выделения пятого поля строки CUT6 Содержит синтаксис для выделения шестого поля строки DISPLAY Определяет, какой режим отображения использовать ENTRY Запись в файле паролей для указанного пользователя EXTRA Данная переменная содержит полный цикл shell-команд, хранимых в виде одной строки KB Входные данные от клавиатуры, полученные в цикле NAME Содержит в каждый данный момент времени одно имя из списка всех регистрационных имен SORT Содержит выполняемый тип сортировки XTRA Флаг, определяющий, должны ли быть активизированы дополнительные опции ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ whox? Как уже ранее обсуждалось в других местах этой книги, система UNIX стремится обеспечить минимум возможностей в любой заданной об- ласти интересов. Это не значит, что UNIX плохая система. Наоборот, она делает гораздо больше, чем большинство других операционных систем. Но очень часто мы хотим сделать немного больше того, что нам предоставля- ет базовая система. Получение информации о том, кто использует машину, может быть применено для многих целей. Основная информация предоставляется коман- дой who, но она может быть не в том виде, который вам нужен для конк- ретной цели. В любом случае, все, что делает команда who - дает вам моментальную информацию о том, кто зарегистрировался. Нам необходим некоторый способ автоматического доступа к этому списку зарегистриро- ванных имен и использования его для наблюдения, общения или других це- лей. ЧТО ДЕЛАЕТ whox? Whox - это инструментальное средство, расширяющее возможности ко- манды who. Оно не только может переупорядочить в соответствии с вашими требованиями список, полученный от команды who, но также может указать каждого зарегистрированного пользователя, передать почтовое сообщение любому зарегистрированному пользователю, просмотреть парольную инфор- мацию всех зарегистрированных пользователей и показать информацию, взятую из индексного дескриптора файла, соответствующего терминальному устройству каждого зарегистрированного пользователя. По умолчанию действие whox заключается в печати обычного выхода команды who в порядке времени регистрации от более раннего до более позднего. Опция -x добавляет к этому списку информацию из регистраци- онного каталога и поле комментария из файла паролей. Если эта опция -x кажется вам знакомой, то так оно и есть, поскольку это то же самое, что и команда info, представленная ранее. Whox имеет четыре различных режима отображения. Первый это формат обычного выхода команды who. Whox позволяет вам сортировать его двумя разными способами. Опция -n сортирует по именам, а опция -t (которую не нужно указывать, поскольку она используется по умолчанию) сортирует по времени регистрации. Второй режим отображения состоит из режимов указания и паролей, включаемых опциями -f и -p. Основное отличие от первого режима заклю- чается в том, что выход команды who не печатается, а используется для генерации списка имен пользователей, который применяется для других целей. Мы указываем каждого пользователя или печатаем парольную запись каждого пользователя. Выполняемая команда хранится в переменной, поэ- тому мы можем иметь общий цикл, использующий особым образом перемен- ные. (Команда finger имеется в системе Berkeley UNIX и в некоторых других, но не во всех реализациях. Посмотрите руководство, чтобы вы- яснить, что выводится на экран по этой команде.) Третий режим - это почтовый режим, в котором вы имеете возмож- ность посылки почтового сообщения каждому зарегистрированному пользо- вателю. Вам задается вопрос о том, действительно ли вы хотите сделать это. Больше от вас ничего не требуется. Этот режим выбирает опция -m. Последний режим - это режим записи на терминал. Режим записи (оп- ция -w) показывает информацию о файле терминала для каждого зарегист- рированного терминального устройства. Эта информация полезна, если вы хотите использовать команду UNIX'а write. Посмотрев на права доступа к файлу устройства пользователя, вы можете сказать, имеется ли у вас возможность записать текст на его экран. Некоторые пользователи, кото- рые не хотят, чтобы их прерывали, закрывают право записи на их терми- нал, выполняя команду "mesg n". Вопрос о праве записи касается любого способа посылки текста в другой файл, а не только с использованием ко- манды write. Право записи также защищает от таких вещей, как "echo hello > /dev/tty00". Способ обработки аргументов в командной строке приводит к несколько странному обращению с данной утилитой. Каждый аргумент про- веряется по порядку и устанавливает внутренние флаги. Если вы постави- те в конце списка какую-либо опцию, меняющую флаг, установленный одной из предыдущих опций, то вы получите действие последней опции. (Другими словами, некоторые опции взаимно исключают друг друга. Лучший способ изучить это - внимательно прочитать исходный текст командного файла и выполнить несколько экспериментов с различными наборами опций.) Например, мы хотим указать каждого пользователя. Мы используем опцию -f. Опция -f устанавливает в качестве режима отображения режим указания. Если мы поместим опцию -w справа от -f, как в команде "whox -f -w", то установится режим записи на терминал. Команда whox будет считать, что вы вообще не указывали опцию -f. На самом деле это не представляет большую проблему, если вы знаете, что делает каждая оп- ция. Случайно смешивая их в одной команде, вы можете получить несколь- ко странные выходные данные.

    ПРИМЕРЫ

1. $ sh -x whox -x Запуск интерпретатора shell в отладочном режиме выполнения, пода- ча ему командного файла whox в качестве данных, передача опции -x для whox. Отладочный режим показывает присвоение значений переменным и вы- зовы команд. (Мы видели это ранее.) 2. $ whox -n -x Печать выходных данных команды who, отсортированных по именам и выдача дополнительных данных.

    ПОЯСНЕНИЯ

В строках 4-10 выполняется инициализация переменных. Переменная XTRA устанавливается в значение "no". Эта переменная используется для построения командной строки при использовании опции -x. Установка XTRA в значение "no" означает, что действие по умолчанию - не получать до- полнительную информацию. Умолчанием для сортировки является сортировка по времени регист- рации. Это указывается сортировкой по колонке, стоящей после второй (+2) и игнорированием ведущих пробелов (-b) в строке 5. Такой же син- таксис применен ниже с опцией -t. Опция -t здесь лишняя, но она делает более понятной командную строку. Опция -n также изменяет синтаксис сортировки, чтобы просматривать первую колонку, которая является списком имен. Строка 6 инициализирует обычный режим отображения, которым явля- ется печать стандартного выхода команды who. Если присутствуют другие опции, соответственно изменяется переменная DISPLAY. Строки 8,9 и 10 инициализируют некоторые переменные для команд вырезки, что делает более компактными последующие команды. Если неко- торые командные строки оказываются слишком длинными, вы можете по- местить нужный текст в переменные и подставить их при необходимости. Переменная CUT1 выглядит так, как будто она должна работать, но она не работает в моей системе. Почему она не работает, объясняется ниже. Просто запомните, что эта строка никогда не используется в данной программе. Мы пока оставляем ее, чтобы поговорить о ней позже. Вы мо- жете убрать ее, если хотите. Строки 12-35 обрабатывают аргументы командной строки. Цикл for подставляет в переменную ARG каждый параметр по порядку и выполняет оператор case по значению ARG. Если опцией является -f, строка 15 изменяет переменную DISPLAY в режим указания. Она также подставляет в переменную COMMAND команду finger, которая выполнится в следующем цикле. Причина, по которой нам нужно иметь переменную, содержащую команду, заключается в том, что данный цикл является общим циклом, применяемым для двух разных целей. Для того чтобы один и тот же цикл справился с двумя различными задача- ми, мы помещаем эти задачи в переменную и выполняем эту переменную. Это значительно сокращает общее количество текста, хотя привносит не- которую дополнительную работу. Обратите внимание в строке 16, что сим- вол $ экранирован в операторе присваивания. Это необходимо, ведь мы хотим, чтобы переменная COMMAND содержала символьную строку $NAME, а не значение, которое имеет переменная NAME после присваивания. Значе- ние NAME расшифровывается в цикле во время его выполнения. Строка 17 обрабатывает опцию -n. Все, что здесь требуется - изме- нить способ сортировки выхода команды who, чтобы отразить порядок по именам. Поскольку имя находится в первой колонке выхода команды who, а команда sort сортирует по умолчанию по первой колонке, то используется команда sort без опций. Строка 18 обрабатывает опцию -m для передачи почтовых сообщений. Здесь мы должны изменить режим отображения на почтовый. Все, что нужно для этого, находится в цикле mail, и не требуется инициализировать ни- какие переменные. Строки 19 и 20 справляются с опцией -p. Опция паролей изменяет режим отображения на парольный режим и устанавливает команду, которую мы вызываем, находясь в общем цикле. В данном случае мы используем ко- манду grep для получения парольной записи из файла /etc/passwd. Обра- тите внимание, что в строке 20 мы используем внутренние двойные кавыч- ки. Для этого мы вынуждены экранировать их символами обратной косой черты. Напомним, что обратная косая черта используется для отмены спе- циального значения особых shell-символов. Строка 21 управляет опцией -t. Как уже упоминалось ранее, опция -t в действительности не требуется в данной программе. Поскольку она является умолчанием, требуемые для нее действия уже были предприняты в начале программы - была выполнена точно такая же инициализация. Син- таксис команды sort точно такой же, как и в строке 5. Строка 22 обрабатывает опцию -w для показа возможности записи в файлы терминалов. Единственное, что нужно здесь сделать - изменить ре- жим работы терминала. Строка 23 управляет опцией -x. Поскольку для получения дополни- тельной информации требуется довольно сложная инициализация, то мы только устанавливаем в этом месте флаг XTRA, показывающий, что мы хо- тим выполнить эту инициализацию позже. Строка 24 - это улавливатель для обработки ошибок. Символ * соот- ветствует любому символу, который не был распознан ранее. Печатается сообщение об ошибке и синтаксическая подсказка, и whox завершается. Строки 37-43 устанавливают переменные, используемые в опции до- полнительной информации. Строка 37 проверяет, установлена ли перемен- ная XTRA в состояние "yes", что имеет место только тогда, когда в ко- мандной строке имелась опция -x. Если это так, то в переменную EXTRA заносится много всяких вещей, которые мы рассмотрим позже. В противном случае в переменную EXTRA заносится пустая строка, так что она никак не проявляется на стадии фактического выполнения. Переменная EXTRA здесь очень важна и на самом деле делает неболь- шой фокус. Мы собираемся поместить в переменную некоторый код, требуе- мый для обработки опции -x. Поскольку дополнительная информация, кото- рую мы хотим получить, требует предварительно некоторой обработки, то мы помещаем в переменную некоторый командный текст. Как только эта пе- ременная начинает выполняться, выполняется и этот командный текст. Это похоже на макрокоманду, только ее текст находится фактически в испол- няемой программе. Строки 38-41 вставлены внутрь переменной EXTRA. Это сделано путем взятия в двойные кавычки всех четырех строк. Все специальные символы, которые должны быть частью данных в этой переменной, должны быть экра- нированы символами обратной косой черты. В строке 38 в переменную EXTRA заносится символ конвейера (|) и начало цикла while. В конце строки 38 имеется символ косой черты, указывающий интерпретатору shell, что присваивание продолжается после символа конца строки (возв- рата каретки или перевода строки). Строка 39 присваивает переменной NAME значение поля, вырезанного из данных, читаемых в цикле while. Напомним, что весь данный оператор помещается внутрь переменной EXTRA. Когда я выше упоминал, что в стро- ке с переменной CUT1 есть проблемы, то как одно из таких проблемных мест я имел в виду именно это. Когда я попытался использовать перемен- ную CUT1 в этом операторе вместо указания команды cut, shell не смог правильно распознать этот оператор. Одинарные кавычки, отмечающие сим- вол-разделитель для вырезки, не были распознаны. В результате команда cut считала, что символом-разделителем является символ ' и после этого аварийно завершалась, поскольку второй символ ' представлял собой не- допустимое описание списка для опции -f. Строка с опцией -f шла позже, но команда cut этого никогда не узнавала, поскольку аварийно заверша- лась до этого. Когда я заменил переменную CUT1 просто командой cut, эта проблема исчезла. Давайте рассмотрим, как я отлаживал эту часть. Я использовал shell с опцией -x, поэтому я мог следить за тем, что происходит. Как вы можете видеть, когда переменная CUT1 была инициализирована, одинар- ные кавычки находились все еще в операторе, но когда выполнялась настоящая команда cut, одинарные кавычки уходили при синтаксическом расширении. Для генерации такого списка данных я выполнил следующий вызов: sh -x whox -x. Вот что я увидел: XTRA=no SORT=sort -b +2 DISPLAY=norm CUT1=cut -d' ' -f1 <- Одинарные кавычки все еще здесь. Основная проблема. CUT5=cut -d: -f5 CUT6=cut -d: -f6 XTRA=yes + who + read LINE + sort -b +2 + echo russ console Jun 20 14:11 + cut -d -f1 <- Теперь выполняется правильно. Кавычек нет. Это сокращенная распечатка. Она показывает, что когда выполнялась команда cut, она не имела одинарных кавычек. Когда же запускалась пе- ременная CUT1, она имела одинарные кавычки. Я не мог представить, как избавиться от кавычек, поэтому я просто вставил вызов самой команды cut обратно на это место. Может быть какой-нибудь молодой растущий мастер сможет себе это представить. Во всяком случае, вы можете видеть полезность отладки. Цикл, выполняющий такое же присваивание, имеет такой вид при обычном стиле записи на языке shell: | while read LINE do NAME=`echo $LINE | cut -d' ' -f1` ENTRY=`grep "^$NAME:" /etc/passwd` echo "$LINE\t\`echo $ENTRY|$CUT6\`\t\`echo $ENTRY|$CUT5\`\" done Для того чтобы поместить такой же цикл в переменную, мы должны экранировать в этом тексте все специальные символы. Строки 45-60 - это оператор case, который реализует различные ре- жимы отображения. Строка 46 выполняет обычный режим отображения коман- ды who. Поскольку в обычном режиме имеется возможность использовать переменную EXTRA, нам необходимо произвести повторный разбор командной строки командой eval, чтобы эта переменная приняла свое истинное зна- чение во время исполнения. Обратите внимание, что в команде eval име- ются кавычки, заключающие всю командную строку. Это необходимо потому, что вся строка является одним набором входных данных для команды eval. Без кавычек команда eval не работала бы. Переменная EXTRA не подверга- ется повторному разбору. Строки 47-50 управляют режимами указания пользователя и выдачи информации из файла паролей. Оба эти режима используют один и тот же цикл. Цикл for использован для установки переменной NAME в значение первого поля каждой строки, полученной от команды who. Для каждого имени, вырезанного из результата работы команды who, выполняется пов- торный синтаксический разбор командой eval переменной COMMAND (которая была установлена в операторе case, выполнявшем разбор аргументов). Тем самым повторно анализируются и выполняются команды, находящиеся в пе- ременной COMMAND. Для режима указания пользователя переменная COMMAND содержит команду finger, а для режима паролей в COMMAND хранится ко- манда grep. Строки 51-58 похожи на режим указания пользователя. Этот цикл то- же требует имена от команды who, но вместо использования оператора for мы используем метод прямой пересылки по конвейеру. Результат работы команды who по конвейеру передается команде cut (переменная CUT1 и здесь бы не работала), которая по конвейеру передает данные в цикл чтения while. Обратите внимание, что в этом месте нет никакой сорти- ровки. По умолчанию результат команды who выводится в порядке номеров терминальных устройств. Я не думаю, однако, что порядок вывода этих данных имеет большое значение. Для каждого имени пользователя выводится запрос о том, хотите ли вы передать ему почтовое сообщение. При чтении ответа в строке 54 должна быть использована команда UNIX'а line. Почему? Потому что весь цикл использует оператор read для чтения имен. Оператор read читает только со стандартного ввода, который в данном случае привязан к кон- вейеру. Для получения входных данных с клавиатуры мы должны использо- вать команду line, которая получает их из файла /dev/tty. Это расп- ространенный способ чтения данных с клавиатуры из переадресованного цикла. Строка 55 проверяет, является ли ответом символ y. Если да, вызы- вается команда UNIX'а mail, и снова ввод переадресовывается из файла /dev/tty (поскольку строки почтового сообщения мы должны вводить с клавиатуры.) В данном случае мы фактически переадресовываем стандарт- ный ввод для вызова подчиненного shell-процесса, выполняющего команду mail. Без выполнения переадресации команда mail читает из файла /dev/null, что нарушает выполнение всего цикла whox. Строка 59 управляет режимом показа возможности записи на терми- нал. Цель здесь такова - использовать одну команду ls и, применяя под- чиненный процесс, извлечь файлы терминальных устройств из выходных данных команды who. Эти файлы являются вторым полем результата команды who. Сначала запускается команда who, которая по конвейеру передает свои данные команде sed. Затем sed использует команду подстановки для отбрасывания всего, кроме того, что ограничено символами \( и \). Последующая часть коман- ды подстановки ссылается на этот ограниченный участок с помощью обоз- начения \1. Используя символ . как соответствующий любому символу распечатки, мы должны всего лишь посчитать столбцы, которые нам нужно вырезать. Кроме того, имена устройств в команде who не имеют префикса /dev/, который нам необходим. Команда sed вставляет его перед текстом, вырезанным из команды who. В результате команде ls дается список пол- ных маршрутных имен ко всем файлам устройств зарегистрированных поль- зователей. Затем это выводится на экран.

    ЗАЩИТА ЛИЧНОЙ ИНФОРМАЦИИ

Другой стороной осведомленности пользователя является обеспечение разумной степени безопасности. Ваша потребность в защите информации зависит от рода выполняемой вами работы и от чувствительности информа- ции, которой вы управляете. Однако все хотят секретности и чувства бе- зопасности, которое появляется вместе с обоснованной уверенностью в том, что они не могут стать жертвой нарушения защиты информации. Так же, как вы можете изготавливать приспособления, помогающие сделать ваш дом менее привлекательным для грабителей, так и каждый пользователь может сделать приспособления, которые помогают поддерживать секретность и безопасность его работы. Подобно полиции, системные администраторы обычно имеют дело только с нарушением защиты уже после свершившегося факта. Вы можете использовать средства, представленные нами в данном разделе, чтобы помочь уберечь ваши файлы от несанкционированного просмотра или разрушения. Любая компьютерная система требует некоторого рода защиты. Уровни защиты включают в себя физическую защиту (центрального процессора, дисков и терминалов), защиту файлов, защиту процессов и всей работаю- щей системы. В многопользовательской среде еще более важно усиливать защиту. Каждый пользователь имеет право засекречивать и защищать свою среду и свои файлы. Ни один компьютер не имеет стопроцентной защиты. Ваша среда лишь настолько защищена, насколько вы сделали ее таковой. Защитные мероприятия могут достигать такой степени, что начинают ме- шать свободному обмену идеями и затруднять использование гибкости системы или исследование ее новых аспектов. Лично я считаю, что поль- зователи должны иметь свободу делать все, что они хотят, пока это не вредит системе или другим пользователям. Большинство информации, при- веденной в данной книге, подтверждает это убеждение. Тип защиты, который обсуждается в данной главе, касается личного аспекта защиты пользователя системы UNIX. Я хочу, чтобы место, где фи- зически находятся мои разработки, мой регистрационный каталог и любые процессы, которые я запускаю в системе, были защищены. Для полного комфорта мне нужно знать, что никто не влезет в мои вещи (посредством обычных методов доступа или взлома) и не будет заглядывать мне через плечо, чтобы наблюдать, чем я занимаюсь. В данном разделе мы рассмотрим инструментальные средства acme, inuse и lock. Acme - это препроцессор к команде UNIX'а acctcom. Acctcom выполняет довольно неплохую работу по отображению учетной ин- формации, но некоторые опции требуется указывать все время. Acme уста- навливает их для нас. Напомним, что учетные записи хранятся в виде структуры, а не в текстовом виде, и поэтому мы заставляем acctcom по- казывать их для нас. Следующее средство, inuse, позволяет вам установить ваш терминал как "занятый", когда вы куда-нибудь уходите. Это эффективно блокирует его и не позволяет никому использовать его. Для реализации такого бло- кирования представлены программы как на языке Си, так и на языке ин- терпретатора shell. Последнее средство, lock, используется для блокирования и разбло- кирования прав доступа к файлу и является на самом деле простым интер- фейсом с командой chmod.
ИМЯ: acme
acme Отображение учетной информации обо мне

    НАЗНАЧЕНИЕ

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

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

acme [-l] [-u]

    ПРИМЕР ВЫЗОВА

acme -u Выводит всю учетную информацию о пользователе с именем $LOGNAME

    ТЕКСТ ПРОГРАММЫ

1 : 2 # @(#) acme v1.0 Give accounting info on me Author: Russ Sage 2а Дать учетную информацию обо мне 4 if [ "$1" != "-l" -a "$1" != "-u" ] 5 then echo "usage: acme [-l] [-u]" >&2 6 echo " -l for ttyline" >&2 7 echo " -u for user name" >&2 8 exit 0 9 fi 11 OPT="" 12 for ARG in $* 13 do 14 case $ARG in 15 -l) OPT="$OPT -l `basename \`tty\``";; 16 -u) OPT="$OPT -u $LOGNAME";; 17 *) OPT="$OPT $ARG";; 18 esac 19 done 21 echo "acctcom $OPT" 22 acctcom $OPT

    ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ

ARG Каждое значение, указанное в командной строке LOGNAME Переменная среды, содержащая мое регистрационное имя OPT Объединенный список всех опций и их аргументов ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ acme? Большинство больших систем UNIX запускают стандартное программное обеспечение для сбора учетной информации об использовании системы. Ре- зультаты учетных транзакций передаются обычно в файл /usr/adm/pacct. Фактически сбор учетной информации выполняется ядром системы. Каждый раз при завершении процесса программы сбора учетной информации в ядре производят одну запись. Переключателем, который включает и выключает эту операцию, является acct(2). Команды пользовательского уровня также взаимодействуют с системным вызовом (accton(1M)) и печатают результаты сбора учетной информации (acctcom(1)). Теперь, когда мы знаем, где находятся учетные записи и как они туда попадают, нам нужно напечатать эту информацию. Acctcom может пе- чатать таблицы с информацией, но вам необходимо знать, какой использо- вать индекс. Просмотр может производится по номеру терминальной линии (это полезно, если идентификатор процесса был изменен командой setuid), по имени пользователя, по группе, по времени и т.д. Я наибо- лее часто использую опции поиска информации по номеру линии терминала и по имени пользователя. С их помощью вы можете получить список всех основных данных, имеющих отношение к вам. Когда вы вызываете acctcom с этими опциями, вам необходимо указать дополнительную информацию, такую как имя вашего терминала и ваше пользовательское имя. Было бы хорошо, если бы мы могли уменьшить количество нажатий на клавиши и объем вы- числений, требуемых для получения информации. Для этого и предназначен acme. ЧТО ДЕЛАЕТ acme? Acme - это интерфейсный процессор для команды acctcom(1). Он слу- жит для выдачи информации, которая требуется согласно указанным опци- ям. Вы должны только дать командному файлу acme опции в сокращенном виде, а все остальное он сделает сам. Если acme вызывается без аргу- ментов, программа acctcom выведет по умолчанию все записи. Команда acctcom имеет много опций. В действительности мы исполь- зуем одну или две, но зато используем их часто. Двумя опциями, которые понимает acme, являются -l и -u. Когда указана опция -l, acme получает имя вашего терминала и помещает его в командную строку. Если указана опция -u, acme получает ваше пользовательское имя и тоже вставляет его в командную строку. Время от времени используются другие опции для бавления специфической информации или небольшого изменения выходного формата. Для того чтобы была возможность использовать другие опции ко- манды acctcom, acme включает в командную строку, формируемую для fcctcom, любые дополнительные корректные опции acctcom, переданные в командной строке для acme. Таким образом, acme поддерживает базовые возможности, а кроме того позволяет вам подгонять команду под ваш вкус. Перед тем, как начнется выполнение команды acctcom, на экран вы- водится расширенный вид командной строки, так что вы можете видеть ко- мандную строку, сгенерированную acme. Без этого может получиться пута- ница, поскольку вы не будете знать, что собирается делать программа.

    ПРИМЕРЫ

1. $ acme Выдача ВСЕХ моих учетных данных. Это последовательный список всех команд, которые были запущены начиная с момента загрузки по настоящее время. Счастливого чтения! 2. $ acme -u -b Печать в обратном порядке всех учетных записей с моим пользова- тельским именем. Обратный порядок означает - от самой последней из предыдущих команд до моей первой команды. 3. $ acme -l Вывод всех учетных записей для терминальной линии, которую я сей- час занимаю. Сюда могут быть включены фоновые процессы, оставшиеся в системе от предыдущих пользователей моей терминальной линии или даже процессы, имеющие другие идентификационные номера пользователей (из-за программ типа setuid, которые я мог запустить), которые запущены с этой же терминальной линии.

    ПОЯСНЕНИЯ

В строках 4-9 выполняется проверка на наличие ошибок. Если пер- вый позиционный параметр не -l и не -u, то это ошибка. Выводится сооб- щение об этом и программа завершается. В строке 11 переменная OPT инициализируется пустой строкой. Эта переменная содержит все дополнительные опции acctcom и их аргументы. Строки 12-19 представляют собой цикл for, который повторяется по всем позиционным параметрам. Каждый аргумент сверяется в операторе case с допустимыми опциями. Если опцией является -l (строка 15), в пе- ременную OPT заносится то значение, которое она уже имеет, опция -l и добавляется имя терминального устройства, полученное от команды UNIX'а tty. Команда tty выводит и префикс /dev, который не нужен. Для того чтобы избавиться от этого префикса, мы берем из этой символьной строки только основное имя. Если указана опция -u, в переменную OPT добавляется -u и наше ре- гистрационное имя. Если аргументом являются любые другие данные, то они просто добавляются в переменную OPT. Поступая таким образом, мы можем передать в командной строке acme другие опции команде acctcom. Обратите внимание, что здесь не выполняется проверка на ошибки в аргу- ментах командной строки. Вы можете ввести неверное значение, которое нарушит работу команды acctcom. Однако это та цена, которую мы вынуж- дены платить за гибкость при передаче аргументов в командной строке, иначе нам придется значительно увеличивать текст командного файла. После того обработки всех опций строка 21 выводит на экран ко- мандную строку, которая должна быть выполнена, так что мы знаем, что мы задавали. В строке 22 выполняется сама команда acctcom. Выходной результат соответствует описанию acctcom(1).
ИМЯ: inuse
inuse Запретить использование терминала

    НАЗНАЧЕНИЕ

Блокирует ваш терминал путем перевода в состояние занятости. Если кто-либо попытается вторгнуться, вы это заметите.

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

inuse

    ПРИМЕРЫ ВЫЗОВА

inuse Перевод терминала в состояние занятости mypasswd Вводится мой пароль, но не отображается на экран

    ТЕКСТ ПРОГРАММЫ

1 : 2 # @(#) inuse v1.0 Disable terminal and alert if used Author: Russ Sage 2а Запретить использование терминала и сообщить о попытке использования 4 trap "echo you\'re BUSTED!!; stty echo; kill $$" 2 15 6 PATH=/bin:/usr/bin 7 SECRET="secret" 9 stty -echo 10 echo "Lock string: \c" 11 read BUF1 12 echo 14 while : 15 do 16 BUF2=`line < /dev/tty` 17 if [ "$BUF2" = "$BUF1" ] 18 then break 19 elif [ "$BUF2" = "$SECRET" ] 20 then break 21 fi 22 echo "^G\c" 23 done 24 stty echo ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ inuse? Рабочий день всегда загружен: много людей, еще больше бумаг, вся- кие поручения и так далее и так далее. Когда вы покидаете свое рабочее место, что вы собираетесь делать со своим зарегистрированным термина- лом? Каждый раз входить в систему и выходить из нее слишком долго, но вы не хотите оставлять вашу работу открытой для всех. Вам необходима программа, которую вы можете запустить на время вашего отсутствия и которая не позволит другим людям использовать то, что вы делаете. Конечно, не достаточно иметь некий процесс, который выполняется в фоновом режиме, создавая впечатление, что терминал используется. Мы должны перехватывать прерывания в случае, если кто-нибудь введет сим- волы "прерывания" или "выхода" с вашей клавиатуры. Единственным спосо- бом разблокирования терминала должен быть ввод пароля или заранее оп- ределенного слова, которое в любом случае разблокирует его. ЧТО ДЕЛАЕТ inuse? Inuse переводит ваш терминал в режим вечной работы. Это означает, что терминал не отвечает на ваши запросы или запросы кого-то другого. Когда вы готовы разблокировать ваш терминал, введите секретный пароль или пароль, который вы придумали. Когда вы первый раз вызываете inuse, у вас запрашивается пароль. Эхо-отображение на терминал отключено, поэтому пароль не выводится на экран. Для гарантии того, что никто не пытается переадресовать команд- ному файлу inuse какой-либо файл данных, все операции чтения произво- дятся непосредственно из файла терминального устройства /dev/tty, а не через файловый дескриптор стандартного ввода. Это позволяет защититься от попыток других пользователей вторгнуться в работу терминала путем посылки на стандартный ввод файла большого размера, содержащего разные слова. После чтения вашего пароля inuse попадает в бесконечный цикл, ко- торый читает символы с клавиатуры и сравнивает полученные входные дан- ные с двумя паролями. Каждый раз, когда кто-то вводит что-то некор- ректное, выдается звуковой сигнал. Когда же введен один из допустимых паролей, программа останавливается и терминал разблокируется. Мы предлагаем две реализации inuse: командный файл интерпретатора shell, приведенный выше, и программу на языке Си. Они выполняют одну и ту же работу довольно похожими способами. Они демонстрируют, насколько похожи эти два подхода, а их небольшие отличия рассматриваются ниже. Сначала обсуждается командный файл, а затем Си-программа.

    ПОЯСНЕНИЯ

Строка 4 инициализирует оператор trap. При активизации обработчи- ка trap выполняются три команды. Целью применения ловушки trap является реагирование на любую по- пытку прервать работу командного файла и прорваться на ваш терминал. Первая команда выдает предупреждение о том, что вы вторгаетесь. Вторая команда переключает терминал обратно в режим эхо-отображения, так что все, что будет впоследствии введено с клавиатуры, отобразится на экра- не. и последняя команда заставляет программу совершить самоубийство. Как мы увидим позже, это самоубийство является особым родом прекраще- ния работы программы. Обращение к "самому себе" в операторе kill вы- полняется с использованием метасимволов $$, которые представляют собой идентификационный номер выполняющегося shell-процесса. Обработчик ло- вушек включается сигналами 2 и 15, которыми являются прерывание и программное завершение соответственно. Отметим, что сигнал выхода из программы (сигнал 3) здесь не используется. Причину этого мы объясним позже. Строка 6 устанавливает маршрут, по которому может обращаться inuse. Тем самым inuse никогда не сможет быть "застигнут врасплох" кем-нибудь, кто проник незаметно (в "троянском коне"). Строка 7 иници- ализирует секретный пароль значением "secret", которое совсем не сек- ретно. Вы можете изменить пароль на любое слово перед установкой ко- мандного файла в вашей системе. В правах доступа к файлу, в котором хранится текст данной shell-программы, вы должны запретить возможность чтения. В противном случае другие пользователи смогут увидеть секрет- ное слово. Строка 9 отключает эхо-отображение, строка 10 печатает запрос па- роля, а строка 11 читает пароль, который вы вводите, и заносит его в переменную BUF1. Строки 14-23 представляют собой вечный цикл while, который можно прервать только вводом правильного пароля. Строка 16 читает ввод с клавиатуры. При нажатии возврата каретки строка 17 проверяет, соот- ветствует ли то, что введено с клавиатуры, паролю пользователя. Если нет, переменная BUF2 сравнивается с секретным паролем. Если какой-то из паролей совпадает, оператор break производит выход из цикла while, тем самым прекращая выполнение программы. Если введенные данные не соответствуют ни одному из паролей, то в строке 22 выдается звуковой сигнал и снова начинает выполняться оператор чтения клавиатуры. Если пароль введен правильно, в строке 24 включается эхо-отобра- жение на терминал и программа завершается. Если происходит прерывание, активизируется оператор trap. Данная операция подробно рассматривается ниже.

    ПОДРОБНЕЕ О ЛОВУШКАХ

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

    ПОЯСНЕНИЯ

Строка 1 помещает документирующую информацию в символьный массив. При наличии этого текста в объектном модуле команда what(1) может вы- нуть его оттуда, чтобы мы могли посмотреть его для идентифицирования нашей программы. Строка 3 подключает файл fcntl.h. Этот файл содержит все определе- ния языка Си для открытия, закрытия, чтения и записи файлов. Строка 4 подключает файл signal.h. Мы используем этот файл для определения пе- ременной SIG_IGN, которая является отметкой игнорирования сигналов (signal_ignore). Строка 5 подключает файл sgtty.h, который мы исполь- зуем для определения всего, что относится к получению информации о терминале посредством вызова ioctl(2). Строка 7 определяет размер секретного пароля. Этот размер не обя- зательно должен быть точно таким, как длина пароля. Этот размер указан для удобства программирования. Строка 8 объявляет размер буфера, в который каждый раз произво- дится чтение с клавиатуры. Хотя 512 символов слишком много для такого считывания, на самом деле чтение прекращается с приходом символа возв- рата каретки. Наличие большого буфера дает нам запас памяти. Строки 9 и 10 определяют управляющие символы звукового сигнала и перевода строки. Строка 14 объявляет некоторые рабочие переменные. Обратите внима- ние, что мы используем регистровые целые. Использование регистровых переменных для ускорения работы - полезный прием. Если вы объявили слишком много переменных по сравнению с количеством регистров в вашей машине, не будет никакой ошибки. Оставшиеся переменные рассматриваются как обычные переменные. Переменная fd используется в качестве файлово- го дескриптора при открытии файла /dev/tty, переменной sig последова- тельно присваиваются значения всех сигналов, а переменная n представ- ляет собой число прочитанных символов. Строка 15 определяет секретный массив. Этот символьный массив со- держит наш секретный пароль, который прямо закодирован в программе. Строка 16 определяет два буфера, в которые мы читаем вводимые символы. Buf1 предназначен для нашего пользовательского пароля, а buf2 для по- пытки ввода пароля, который считывается, когда мы хотим прекратить вы- полнение программы. Строка 17 определяет две рабочие структуры, кото- рые содержат информацию об установках терминала (ioctl). Здесь у нас две структуры, поскольку одна из них - первоначальная, а вторая - та, на которую мы хотим изменить, чтобы не забыть первоначальные установ- ки. Строки 19-25 загружают пароль в секретный массив. Мы выполняем посимвольное присвоение, поскольку при таком присвоении любая строка символов в объектном модуле получается разорванной. Это мера безо- пасности для предотвращения возможности зрительного просмотра с целью извлечения ценной информации. В строке 27 эти два буфера инициализируются в нулевой размер. Строки 28 и 29 открывают устройство /dev/tty. Если возвращаемый дескриптор файла равен -1, это говорит об ошибке и программа заверша- ется. Строки 31 и 32 перехватывают все сигналы. Цикл for работает с сигналами, имеющими номера от 2 до 15. Для каждого из этих значений выполняется системный вызов signal с целью игнорирования сигналов с такими значениями. В строках 34-39 выполняется модификация терминальных характе- ристик для отключения эхо-отображения символов. Строка 34 получает ин- формацию об установках терминала в структуру sav_tty. Системный вызов gtty - это просто программный интерфейс с системным вызовом ioctl(get_values). Если этот вызов неудачен, программа завершается. Строка 36 заносит данные из структуры sav_tty в структуру chg_tty. Затем строка 37 присваивает элементу sg_flags результат опе- рации отрицания над его же значением и символом ECHO, что означает "отключить эхо-отображение". После этого строки 38 и 39 записывают из- мененные значения обратно на терминальное устройство. Системный вызов stty - это просто программный интерфейс с системным вызовом ioctl(set_values). Строка 41 выводит на экран запрос на ввод пароля. Дескриптор фай- ла 1 является стандартным устройством вывода, а 13 - длина строки сим- волов. Строка 42 читает BSIZ символов из файла /dev/tty. После чтения на экран выдается символ перевода строки. Это необходимо сделать, поскольку при отсутствии эхо-отображения на экран не выводится символ перевода строки, когда вы вводите свой пароль. Поэтому мы вынуждены вставить этот символ здесь сами. Строки 45-54 представляют собой бесконечный цикл, который читает символы с клавиатуры. Строка 46 выполняет чтение терминала для распоз- навания пароля. В этой строке введенный пароль помещается в buf2, а не в buf1. Мы выясняем количество символов, прочитанных в buf2 (n). Поскольку индексирование массивов начинается с нуля, а не с 1, при вводе n символов мы попадаем в конец текста и здесь мы вставляем ноль для того, чтобы все, что было введено, представляло собой строку сим- волов. Мы делаем это потому, что команда read не производит обработку символьной строки. Это делают системные вызовы stdio. Их описание на- ходится в разделе (3) руководства по системе, а не в разделе (2). Нам нужно оформить прочитанные символы в виде строки, чтобы ее можно было сравнить с паролями. Строка 49 сравнивает то, что ввели с клавиатуры, с тем паролем, который вы ввели в начале работы программы. Если эти символьные строки одинаковы, strcmp возвращает значение ноль, которое сообщает о совпа- дении. Команда break выводит выполнение из цикла for, и программа про- должается. Строка 51 выполняет такое же сравнение с секретным паролем. Если происходит совпадение, вы также выходите из цикла. Если совпадения не произошло, строка 53 выдает на терминал звуко- вой сигнал и управление передается оператору read в начало цикла for. Если произошел выход из цикла for, управление передается строке 55. Происходит запись первоначальной информации об установках термина- ла, тем самым включается эхо-отображение. Строка 56 закрывает файл /dev/tty, и происходит нормальное завершение работы программы. Вы могли видите, программа на языке Си, хотя и несколько сложнее, чем на языке shell, но имеет некоторые преимущества. Она не может быть остановлена или нарушена ничем, кроме команды kill -9. Пароль в испол- няемом модуле спрятан от любопытных глаз. Вы получаете больший уровень защиты и более ясный подход за счет использования языка низкого уровня и написания более длинной программы.
ИМЯ: lock
lock Блокирование и разблокирование файлов

    НАЗНАЧЕНИЕ

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

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

lock [-u] file [...]

    ПРИМЕР ВЫЗОВА

lock $HOME Отключить возможность записи для меня и возможность чтения/записи для группы и других пользователей по отношению к моему регистрационно- му каталогу.

    ТЕКСТ ПРОГРАММЫ

1 : 2 # @(#) lock v1.0 Lock and unlock files Author: Russ Sage 2а Блокирование и разблокирование файлов 4 if [ $# -eq 0 ] 5 then echo "lock: incorrect argument count" >&2 6 echo "usage: lock [-u] file [...]" >&2 7 exit 1 8 fi 10 if [ "`echo $1 | cut -c1`" = "-" -a "$1" != "-u" ] 11 then echo "lock: invalid argument $1" >&2 12 echo "usage: lock [-u] file [...]" >&2 13 exit 1 14 fi 16 MODE1="go-rw" 17 MODE2="u-w" 19 if [ "$1" = "-u" ] 20 then shift 21 MODE1="go+r" 22 MODE2="u+w" 23 fi 25 chmod $MODE1 $@ 26 chmod $MODE2 $@

    ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ

MODE1 Режимы доступа к файлу, относящиеся к группе пользователей и другим пользователям MODE2 Режимы доступа к файлу, относящиеся к владельцу ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ lock? Все файлы в системе UNIX имеют некоторые права доступа. Эти режи- мы изменяются в соответствии с тем, как используется этот файл. Для каждого файла должны быть установлены три набора прав доступа - для владельца, группы и других пользователей. Обычно текстовые файлы имеют режим 644, а исполняемые и каталоги - 755. Некоторые системы присваи- вают по умолчанию другие значения. Если вы хотите ограничить права чтения или записи, следует использовать команду chmod(1). Новый режим должен указываться либо как абсолютное восьмеричное число (например, 777), либо как буквенное вы- ражение, указывающее, какая категория пользователей что может делать (например, ugo+rwx). Если вы хотите добавить или запретить определен- ные возможности, легче использовать для этого буквенное выражение. Но даже в таком случае нам будет полезно средство, позволяющее уменьшить число нажатий на клавиши и избавляющее от необходимости точно запоми- нать все, что касается прав доступа к файлу. ЧТО ДЕЛАЕТ lock? Lock - это средство, которое управляет правами доступа, обеспечи- вающими безопасность всех ваших файлов. Этот командный файл обеспечи- вает необходимые режимы доступа пользователей или запрета доступа в ограниченной степени. Имея заранее определенные режимы доступа, наши файлы лучше сохраняются в безопасном состоянии. Входными данными для lock являются имена файлов. Допускается использование символьной строки с любым набором имен файлов. В ней должно находиться по крайней мере одно имя файла. Имена файлов с ука- занием каталогов также допускаются. Действие lock по умолчанию - блокирование указанного файла. Опция -u разблокирует указанный файл. Если команде chmod передано неверное имя файла, это создает проб- лемы для нее и в этом случае выводится сообщение об ошибке.

    ПРИМЕРЫ

1. $ lock -u $HOME/src *.c Разблокирование моего каталога с исходными текстами и всех исход- ных файлов на языке Си в текущем каталоге. Разблокирование дает воз- можность чтения всем и возможность записи только мне. 2. $ lock $HOME/bin Блокирует мой каталог bin так, чтобы никто не мог читать или писать в него файлы. Даже хотя мой каталог нельзя читать, любой посто- ронний может все же войти в него командой cd, если установлен бит x. Если он попытается выполнить команду ls, каждый файл будет выдавать сообщение об ошибке вида "filename not found" (файл с именем "filename" не найден). Никто не может получить информацию из индексно- го дескриптора файла, такую как временные характеристики и права доступа, но любой может увидеть имена всех файлов из сообщения об ошибке.

    ПОЯСНЕНИЯ

Строки 4-8 проверяют счетчик аргументов. Если не был указан ни один аргумент, выводится сообщение об ошибке. Должно быть указано хотя бы одно имя файла. Строки 10-14 проверяют, является ли первый символ первого позици- онного параметра знаком "минус" и отличается ли первая опция от до- пустимой опции -u. Если эти условия выполняются, выводится сообщение об ошибке и программа завершается. Строки 16 и 17 инициализируют установки режимов прав доступа по умолчанию. MODE1 устанавливается для запрета чтения и записи категори- ям пользователей "группа" и "другие". MODE2 устанавливается для запре- та пользователю (т.е. мне) права записи. Это страховка для меня от случайной записи в файл. Нам нужны две такие переменные, поскольку эти два режима довольно разные. Единственный способ сделать это - дважды вызвать команду chmod с двумя различными установками. Строки 19-23 проверяют, была ли указана в командной строке опция -u. Если была, она убирается из командной строки командой shift и пе- ременные режима инициализируются для разблокирования файлов. Строка 21 разрешает возможность чтения группе пользователей и другим. Строка 22 разрешает мне возможность записи. Обратите внимание, что в командном файле lock не происходит модификации битов x, s или t. Это сделано на- меренно, поскольку бит x должен быть установлен только в случае, если файл может быть исполняемым. Для каталогов бит x должен быть установ- лен только в случае, если вы хотите, чтобы другие пользователи могли заходить в этот каталог. Мы также никогда не устанавливаем возможность записи для группы пользователей и для других пользователей, но мы отк- лючаем ее при блокировании файлов. Это дополнительная мера предосто- рожности на случай, если файл имеет установленными такие права доступа к нему, которые мы по каким-то причинам не желаем оставлять. В строках 25 и 26 выполняется команда chmod. Параметр $@ исполь- зован как обозначение всех имен файлов, указанных в командной строке. Такой способ позволяет вам вызывать lock с несколькими именами файлов. Из последних шести глав вы должны приобрести множество идей о том, как можно обезопасить среду, в которой вы работаете, как иметь дело с другими пользователями и вообще более продуктивно использовать ваш рабочий день. Мы готовы двигаться дальше в понимании системы UNIX и исследовании внутренней работы файловой системы, устройств и комму- никаций.

    * ГЛАВА 7. Устройства и файловые системы *

ТЕРМИНАЛЬНЫЕ УСТРОЙСТВА РАБОТА С КЛАВИАТУРОЙ ФАЙЛЫ termcap ДИСКОВЫЕ УСТРОЙСТВА ФАЙЛОВЫЕ СИСТЕМЫ ЗАГРУЗОЧНЫЙ ДИСК РАЗМЕРНЫЕ ПАРАМЕТРЫ c Быстрая очистка экрана mntf Монтирование гибкого диска в системном дереве mntlook Поиск всех монтированных файловых систем umntsys Размонтирование всех файловых систем, кроме корневой lrgf Создание файла максимального размера, допустимого в вашей системе

    УСТРОЙСТВА И ФАЙЛОВЫЕ СИСТЕМЫ

    ВВЕДЕНИЕ

Ниже уровня известной нам области файловых систем находится мир устройств и их драйверов. В данной главе мы исследуем некоторые мето- ды, необходимые для работы с терминалами, дисками и непосредственно файловыми системами. Программное средство 'c' иллюстрирует доступ к терминалу на примере операции быстрой очистки экрана. Следующие три средства - mntf, mntlook и umntsys - имеют дело с монтированием и раз- монтированием файловых систем. Наконец, средство lrgf позволит вам проверить пределы емкости вашей файловой системы. СИСТЕМА UNIX И АППАРАТУРА Перед тем, как углубиться в сущность вопроса, давайте обсудим не- которые элементарные факты, которые мы должны помнить при рассмотрении всех составляющих системы UNIX. Сердцем аппаратуры является централь- ный процессор (CPU), который исполняет инструкции, управляющие маши- ной, и фактически осуществляет всю работу. Операционная система необ- ходима для руководства работой, выполняемой центральным процессором, и для обеспечения интерфейса между ним и ресурсами, требуемыми для того, чтобы сделать что-то полезное: оперативной памятью, внешней памятью и другими периферийными устройствами, такими как терминалы и принтеры. Операционная система, особенно такая высокоразвитая, как UNIX, имеет множество утилит и характерных особенностей, но сейчас речь не об этом. Сердцем операционной системы (в данном случае UNIX) является ядро (kernel). Ядро управляет процессами и руководит выполняемой рабо- той. Оно также является своего рода мостом между аппаратурой и внешним миром. В данной главе мы обратим внимание на основные взаимоотношения между ядром, процессами и аппаратурой. В конечном итоге система должна взаимодействовать с внешними уст- ройствами. Наличие базовых знаний об устройствах весьма важно для пол- ного понимания того, как UNIX общается с внешним миром. При работе с машиной много времени тратится на передачу данных в машину и из нее, а это значит, что необходимо иметь дело со множеством различных типов устройств, каждое из которых имеет свой "характер" и особенности. К нашему счастью, UNIX был разработан так, чтобы облегчить управ- ление данными и устройствами настолько, насколько это возможно. К на- шему несчастью, имеется, по всей видимости, несократимый объем знаний, которыми мы должны овладеть обязательно. На рис. 7-1 показана общая структура операционной системы UNIX. Мы видим, что со стороны ядра об- ращение ко всем внешним периферийным устройствам выполняется как к файлам устройств. Каждый тип устройств имеет свой собственный драйвер и специфическую архитектуру, но обращение к каждому устройству выпол- няется одинаковыми методами. Мы увидим, как использовать различные способы доступа к устройствам и определим, какие способы наиболее эф- фективны. Рис. 7-1 Модель среды системы UNIX
+-----------+ | | +------------+ | Магнитная | | Принтер | | лента | +------------+ +-----------+ /dev/lp0 lpn /dev/rmt0 .../dev/rmtn \ | | / \ | | / \ | | / \ \ | | | | / / +-------------------+------- /dev/fd0 fd:1,2 | +-------------+ |--- +-----------+ /dev/tty00----| | ПРОЦЕССОР | | |Гибкий диск| +--------+ -- | | ЯДРО | |--- +-----------+ | | / | +-------------+ |-------- fdn | Экран | +-------------------+ +--------+ / / | | | \ \ +--------+ / | \ |Клавиат.| ttynn /dev/hd01 hdnn +--------+ +----------+ fd:0 | | | Жесткий | | диск | | | +----------+
UNIX обращается к периферийным устройствам через "специальные файлы". Имеется два типа специальных файлов: блочные и символьные. Оба типа имеют свое предназначение и особенности. Блочный (например, /dev/hd0) использует буферизацию и позволяет получить доступ к большим объемам данных на жестком диске. Символьный (например, /dev/tty00 или /dev/rfd0) не использует значительную буферизацию, а выполняет обмен с устройством по одному символу за обращение. Даже несмотря на особые свойства таких файлов, для них поддерживается все тот же механизм за- щиты, что и для всех других файлов в системе. Первая область, которую мы рассмотрим - терминальные устройства и работа с ними. Представленные программы включают в себя средство под названием 'c' для быстрой очистки экрана, а также пример программы, которая считывает значения нажатых клавиш и выполняет опрос нажатия одной клавиши. Мы также рассмотрим пример файла описания терминала (termcap), который обеспечивает доступные определения характеристик терминала. Затем мы рассмотрим дисковые устройства - жесткие и гибкие диски. Мы увидим, что имеются различные способы просмотра разделов диска с использованием файлов устройств. В дополнение к работе с устройствами мы рассмотрим файловые системы на жестком диске. Всем нам известно, что система UNIX сущест- венно ориентирована на диски, поэтому чем больше мы знаем о файловых системах, тем лучше для нас. Для более полного понимания разделов диска и файловых систем мы представим три программных средства. Средство lrgf проверяет граничные значения параметров файловой системы путем создания файла наибольшего возможного размера в вашей системе. Средство mntf обеспечивает удобный способ монтирования и размонтирова- ния гибких дисков. Наконец, средство mntlook выполняет поиск немонти- рованных файловых систем, которые представляют собой потенциальную опасность.

    ТЕРМИНАЛЬНЫЕ УСТРОЙСТВА

Драйверы терминальных устройств являются одними из самых сложных драйверов устройств. Причина этого заключается в том, что существует множество уровней программного обеспечения, которые поддерживают ха- рактеристики интерактивных терминалов. При работе терминала по после- довательной линии связи необходима мощная поддержка для того, чтобы облегчить его использование. Различные установки, которые может иметь терминал, программируются командами stty(1) и ioctl(2). Команда termio(7) также описывает различные аспекты протокола работы термина- ла. ПРОТОКОЛ ОПЕРАЦИЙ ВВОДА/ВЫВОДА ТЕРМИНАЛА Протокол работы терминала представляет собой согласованный набор сигналов, позволяющих системе правильно интерпретировать вводимые с клавиатуры строки. Протокол необходим по четырем причинам. Первой яв- ляется поддержка входной обработки специальных символов, таких как символы удаления и прекращения работы программы. Во-вторых, нам необ- ходимо поддерживать обработку выводимой информации, например, вставку символов задержки или изменение последовательности возврат каретки/пе- ревод строки. Третьей причиной является поддержка режимов необработан- ного и "канонического" ввода. Эти два режима позволяют пользова- тельским программам получать данные или по одному символу, или по од- ной строке. Последняя причина введения терминального протокола - жела- ние сделать так, чтобы пользователь мог сам изменять параметры конфи- гурации терминала. Содержимое терминальной подсистемы показано на рис. 7-2. Рисунок разбит на три части: слева - область пользователя, посредине - область ядра и справа - область устройства. Этот рисунок показывает, как пере- даются данные между терминалами и программами пользователя. Рис. 7-2. Управление протоколом терминала
Область Область ядра Область пользователя устройства Процесс +-------+ : : |Текст | : +--------+ +--------+ +-------+ : dzrint() |-------| : ttread() |канонич.| canon()|необраб.| ttin()|приемн.| : +-----+ |Данные | : /---|очередь |<-------|очередь |<------|буфер |<--|dbuf | | | : / +--------+ +--------+ / +-------+ : | | | +----+| : / структура структура / структура : +-----+ | |ubuf||<---/ clist clist / ccblock : | +----+| : / : | +----+| : +--------- / ttxput() : | |ubuf||---- | : | +----+| : \ +-------+ <--+ +--------+ : +-----+ |-------| : \ |выходн.| |буфер | : |dbuf | |Стек | : \--->|очередь|------->|передачи|------------------->| | | | : +-------+ ttout()+--------+ dzxint() : +-----+ +-------+ : ttwrite() структура структура : clist ccblock /|\ | ------+ Граница драйвера устройства ------------------------------------------------------------------- Когда какой-либо процесс читает символы с устройства, данные на- чинают двигаться от буфера драйвера устройства, который называется dbuf. Из этого буфера данные попадают в приемный буфер, управляемый ядром. Приемный буфер читается подпрограммой ядра с именем ttin(), и данные помещаются в структуру clist, называемую необработанной оче- редью. (Слово "необработанная" означает, что над символами пока что не производилось никакой обработки.) В то же время символы также помеща- ются в выходную очередь, что позволяет системе выполнять эхо-отображе- ние символов по мере их ввода. После этого подпрограмма canon() превращает необработанную оче- редь в каноническую. ("Каноническая" означает применение правил, кото- рые в данный момент установлены системой для выполнения специальных функций над строкой текста, например для обработки символа удаления.) Такая обработка позволяет преобразовать данные перед тем, как их полу- чит процесс пользователя. Последней подпрограммой является ttread(), которая читает символы из канонического буфера в буфер пользова- тельского процесса в области данных процесса. Когда символы записываются из процесса пользователя на терминал, они проделывают почти такой же маршрут в обратном направлении: от об- ласти процесса к области драйвера устройства. Основное отличие в подп- рограмме записи заключается в том, что эти данные проходят на один бу- фер меньше. От процесса пользователя символы передаются в выходную очередь ядра подпрограммой ttwrite(), а затем в буфер передачи с по- мощью подпрограммы ttout(). Из буфера передачи они пересылаются не- посредственно в приемный буфер драйвера устройства с помощью подпрог- раммы dzxint().

    ОПРЕДЕЛЕНИЕ ВВОДИМЫХ СИМВОЛОВ

Бывает так, что мы хотим увидеть, какие символы вводятся с клави- атуры. Мы могли бы для этого написать программу, но UNIX предоставляет встроенную возможность для этой цели. Это команда od - восьмеричный дамп (octal dump). Такое название осталось еще с тех давних времен, когда восьмеричное исчисление широко применялось при отладке. К счастью, результат работы команды od можно получить в символьном, шестнадцатиричном или десятичном виде. Фокус использования команды od для проверки входных и выходных значений зак- лючается в том, что od читает стандартное устройство ввода по умолча- нию, если не указан файл. Например, вызов $ od -cx test string ^d ^d даст такой результат:
| | 0000000 6574 7473 7320 7274 6e69 0a67 | t e s t s t r i n g \n | 0000014 | $ | Здесь вызов команды od делается без указания имени файла в ко- мандной строке и с применением стандартного вывода в качестве выводно- го устройства. Мы используем опцию -cx для того, чтобы байты интерпре- тировались как символы ASCII, а соответствующие 16-битовые слова отоб- ражались в шестнадцатиричном виде. По мере того, как вы набираете сим- волы, они отображаются на экране, а команда od сохраняет их в своем буфере. В конце строки нажмите возврат каретки, затем CTRL-D. Ввод CTRL-D завершает чтение символов командой od и выдает распечатку, в которой сверху будут шестнадцатиричные значения, а снизу символы в ко- де ASCII. Обратите внимание, что два символа, выводимые для каждого шест- надцатиричного слова, располагаются в обратном порядке по сравнению с двумя байтами, образующими это слово. Например, слово 6574 интерпрети- руется как два символа, t и e, где 65 - код ASCII для символа e, а 74 - ASCII-код для символа t. Для того чтобы выйти из команды od, введите еще один CTRL-D для прекращения блочного чтения. Если вы хотите еще проверять символы, продолжайте их вводить. Команда od работает несколько загадочно. Если вы введете достаточное количество символов, она выдаст на экран информацию по нажатию только лишь возврата карет- ки. Но если вы ввели всего несколько символов, требуется нажатие как возврата каретки, ТАК И CTRL-D для получения результата на экране. Теперь мы можем сделать один фокус - изменить канонический способ обработки при чтении символов командой od. Это позволит нам увидеть эффект от различных установок протокола работы. Для этого проверьте текущие установки вашего терминала. В версии System V используйте ко- манду "stty -a", а в версии Berkeley вам нужно применить команду "stty everything". System V выдает гораздо больше параметров, чем Berkeley. (Наиболее популярные версии UNIX'а разработаны и поддерживаются следу- ющими фирмами: System V - фирмой AT&T Bell Laboratories, которая в настоящее время называется Unix System Laboratories; BSD (Berkeley Software Distribution) - Калифорнийским университетом в Беркли; XENIX - фирмой Microsoft.- Прим. перев.) Ниже приводится пример из XENIX:
| | speed 9600 baud; line = 0; | intr = DEL; quit = ^|; erase = ^h; | kill = ^u; eof = ^d; eol = ^` | parenb -parodd cs7 -cstobp hupcl cread -clocal | -ignbrk brkint ignpar -parmrk -inpck istrip -inlcr -igncr icrnl -iuclc | ixon ixany -ixoff | isig icanon -xcase echo echoe echok -echonl -noflsh | opost -olcuc onlcr -ocrnl -onocr -onlret -ofill -ofdel ff1 | После того, как мы почешем голову, мы увидим, что текущий флаг для канонической обработки установлен в состояние "icanon". То есть, мы можем видеть, что он включен, поскольку нет префикса в виде знака минус (хотя это не является строгим правилом). Что происходит при канонической обработке? Символ возврата на шаг назад (backspace) является одним из важных вопросов. Когда вы вводите символ CTRL-H, он поступает в необработанную очередь как литеральный символ CTRL-H. Когда программа canon() читает CTRL-H, она понимает это так: "Изменить CTRL-H на символ возврата на шаг назад, записать пробел на место символа, затем сделать еще шаг назад." При эхо -отображении вы получаете удаление символа на экране. Когда каноническая обработка отключена, вы посылаете CTRL-H как обычные символы. Вот пример того, как это выглядит:
| | $ stty -icanon Отключение канонической обработки | $ od -cx | test string^h^h^h^h^h^hcase | ^d... | | 0000000 6574 7473 7320 7274 6e69 0867 0808 0808 | t e s t s t r i n g \b \b \b \b \b | 0000020 6308 7361 0a65 0a04 0a0a 0a0a 0a0a 0a0a | \b c a s e \n 004 \n \n \n \n \n \n \n \n \n | После слова "string" вы видите группу чисел 08, которые в ASCII-коде обозначают CTRL-H. Эти числа 08 показывают вам, что лите- ральные символы CTRL-H действительно присутствуют в их "необработан- ной" форме. Поскольку CTRL-H не является больше специальным символом, команда od рассматривает его подобно любому другому символу. Здесь возникает новая проблема: поскольку специальные символы не распозна- ются, мы потеряли возможность завершить блочное чтение вводом символа конца файла (EOF). Когда вводится CTRL-D, он понимается просто как еще один символ. Поэтому мы должны заполнить буфер команды od, чтобы заставить ее выполнить дамп. В нашем примере CTRL-D - это символ 004 после символов case \n. Кстати, в системе Berkeley используются установки "обработанная" ("cooked") и "необработанная" ("raw") для stty, которые по существу служат для тех же целей, что "canon" и "-canon" в System V.

    ДИНАМИЧЕСКОЕ ПЕРЕОПРЕДЕЛЕНИЕ СИМВОЛОВ ПРЕРЫВАНИЯ

Обратите внимание, что в предыдущей распечатке команды stty -a присутствуют определения символов "intr" и "quit". Это две функции, которые прерывают работу ваших работающих процессов. Строки intr и quit представляют собой особые функции, а не просто нажатие клавиши. Эти функции можно назначить на любую клавишу клавиатуры при помощи ко- манды stty. Если мы изменили значение "intr" на другой символ, то этот новый символ прервет ваши процессы. Вы даже можете установить в качестве клавиши прерывания обычный символ. Вот как это можно сделать: $ stty intr x $ this is a junk stringx $ Когда вы вводите символ x в конце строки, то вся введенная строка обрывается и вы получаете новый символ приглашения. Для того чтобы вернуться к обычному режиму работы, введите в качестве символа преры- вания старый символ. Если старым символом был "delete", вы даете такую команду: $ stty intr DEL Что же в этом хорошего? Это показывает, насколько гибко работает команда stty с терминалом, и может быть использовано в качестве личной меры безопасности, чтобы сбить с толку человека, который захотел бы произвести какой-либо беспорядок с вашего терминала. Когда вам нужно на минуту отойти от терминала, измените ключ прерывания на какой-либо другой и запустите командный файл вроде такого: while : do : done Этот бесконечный цикл будет выполняться постоянно. Если кто-ни- будь попытается прервать этот процесс, используя клавишу DEL, ничего не произойдет. Когда вы вернетесь, нажмите новую клавишу прерывания. Она прервет цикл, и вы получите символ приглашения. Очень важным является также символ "eof". Он соответствует концу файла (end of file), и обычно им является CTRL-D. Это объясняет, поче- му вы выходите из системы UNIX с помощью CTRL-D. Когда вы посылаете CTRL-D вашему регистрационному shell, вы тем самым говорите ему: "Shell, это метка конца файла для данного сеанса работы". Поскольку терминал рассматривается как файл, CTRL-D закрывает файл, а shell, ко- торый работает как цикл вида "читать команды с клавиатуры, пока не встретится EOF", завершается, тем самым посылая сигнал программе init. Программа init порождает команду getty по данной терминальной линии, которая выводит на экран приглашение "login:" для ожидания команды от следующего пользователя. Если вы измените символ конца файла, вы боль- ше не сможете выйти из shell по CTRL-D. Он будет выводиться на экран точно так же, как любой другой символ. Вот пример таких действий: $ stty eof x $ x login: Теперь признаком конца файла является обычный символ x. Когда вы вводите символ x, это равносильно вводу CTRL-D, и вы выходите из системы. Очевидно, такие манипуляции нежелательны, однако это позволя- ет показать ту большую степень гибкости, которую использует UNIX при присвоении различных функций разным символам.

    ФАЙЛЫ ТЕРМИНАЛЬНЫХ УСТРОЙСТВ

Физически адресация терминалов производится посредством файлов устройств в каталоге /dev. Когда вы регистрируетесь в системе, вам присваивается определенный номер терминала, например tty01. Этот номер терминала в действительности является файлом /dev/tty01. Если вы вызо- вете команду tty UNIX, она выведет полное маршрутное имя файла того терминального устройства, за которым вы работаете. Файлы терминальных устройств выглядят так же, как обычные файлы, за исключением того, что команда "ls -l" показывает, как называются старший и младший номера устройства, которые не являются частью обыч- ных файлов. Старший номер является индексом в таблице cdevsw[], кото- рая содержит адрес драйвера устройства, используемого ядром для данно- го типа устройства. Младший номер идентифицирует конкретное физическое устройство. Эти номера появляются в последовательном порядке для всех устройств, использующих один и тот же драйвер. Так выглядит типичный список файлов устройств в системе XENIX:
| | crw--w--w- 1 russ tricks 0, 0 Jun 22 02:34 /dev/console | crw--w--w- 1 russ tricks 0, 1 Jun 22 00:41 /dev/tty02 | crw--w--w- 1 root tricks 0, 2 Jun 21 17:56 /dev/tty03 | crw--w--w- 1 root tricks 0, 3 Jun 21 05:47 /dev/tty04 | crw-rw-rw- 1 root root 0, 4 Feb 18 17:09 /dev/tty05 | crw-rw-rw- 1 root root 0, 5 Feb 18 17:09 /dev/tty06 | crw-rw-rw- 2 root root 5, 0 Jun 21 20:23 /dev/tty11 | crw--w--w- 2 root tricks 5, 8 Jun 22 02:20 /dev/tty12 | crw-rw-rw- 2 root root 5,128 Feb 18 17:09 /dev/tty13 | crw-rw-rw- 2 root root 5,136 Feb 18 17:09 /dev/tty14 | По символу 'c' в первом столбце мы видим, что это символьные уст- ройства, а биты прав доступа показывают, кто имеет доступ к этим фай- лам. Первый столбец чисел (0 или 5) является старшим номером. Младшие номера в следующем столбце обычно следуют в последовательном порядке, но не всегда (как видно в данном примере). В дополнение к использованию абсолютного номера вашего терминала, одно из устройств используется в качестве "логического", или "родово- го" адреса вашего терминала. Оно использует другой драйвер устройства, называется /dev/tty и применяется в случаях, когда стандартный ввод и стандартный вывод переадресовываются в другие файлы, а прикладной программе необходимо читать с клавиатуры или писать на экран. При по- мощи доступа к файлу /dev/tty образуется связь с самим терминалом. Вы- бор использования устройства с именем tty вместо устройства tty01 главным образом зависит от того, какая стоит задача. Если вам необхо- димо иметь независимую от типа терминала программу, используйте /dev/tty.

    ПРАВА ДОСТУПА К ТЕРМИНАЛУ

Поскольку терминальное устройство является файлом, оно имеет ре- жимы прав доступа точно так же, как и все другие файлы. Эти режимы представляют собой защиту доступа к вашему терминалу. Если все пользо- ватели имеют право записи на ваш терминал (это обозначается как rw--w--w-), то они могут записать на ваш экран все, что угодно, и вы никогда не узнаете, кто это сделал. Если вы хотите предупредить это, то выполните команду "chmod 600 `tty`", где символы ударения и обозна- чение tty соответствуют маршрутному имени вашего терминального файла. Более простой в использовании является команда UNIX'а mesg. Команда "mesg n" запрещает запись извне на ваш терминал. Ваши собственные про- цессы по-прежнему имеют доступ на запись. Права доступа к терминалу связаны также с проблемой безопасности, которую мы рассмотрим в главе 9. Пока что отметим, что всякий раз, когда вы открываете файл (то ли для чтения, то ли для записи), вам возвращается дескриптор файла. Затем вы можете использовать этот деск- риптор файла в системном вызове ioctl. Получение этого дескриптора файла подобно получению ключа к терминальному интерфейсу определенного пользователя. Любые изменения, производимые с помощью ioctl с данным дескриптором файла, вызывают немедленный эффект, и нарушитель защиты может читать все, что записывается или считывается с вашего терминала или даже заставить ваш терминал выполнять команды, которые присваивают себе неразрешенные права! Пользователь, который работает с этим терми- налом, может никогда не узнать, что же произошло или кто это сделал. Другим примером подобного рода является команда write(1). Она используется для установки связи по линии в реальном режиме времени, или "болтовни". Она общается с терминалом путем выполнения записи в файл устройства. Измените биты прав доступа, выключив их командой "mesg n", и никто не сможет выполнить команду write с вашим термина- лом. Таким способом вы можете "снять трубку телефона", когда вы хоти- те, чтобы вам не мешали. Вместе с тем кто-нибудь мог бы сделать такое: $ while : > do > clear > /dev/tty00 > done & При этом создается фоновый бесконечный процесс (пока он не будет прек- ращен командой kill или выходом из системы), который посылает пользо- вателю терминала tty00 символы очистки экрана. Как только этот пользо- ватель что-нибудь набирает на экране, он тут же очищается. Большинство пользователей не могут даже понять, что происходит. Если это случится с вами, попытайтесь отключить права доступа к вашему терминалу. Если же тот, кто это делает, является суперпользователем (root), то никакие права доступа не смогут остановить его, поскольку суперпользователь не имеет ограничений по правам доступа к файлам. В этом случае возникает проблема для системного администратора! Еще одна странная вещь наблюдается в случае, когда кто-либо посы- лает что-то на ваш терминал командой write, а вы отключаете права доступа. Команда write по-прежнему имеет доступ к вашему терминалу, пока она не закроет устройство. После закрытия она больше не имеет прав для открытия устройства. Представляется странным факт, что после получения доступа к терминалу последующее запрещение права доступа не оказывает никакого воздействия до тех пор, пока вы не прекратите рабо- ту с терминалом.

    ОТМЕТКИ О ВРЕМЕНИ ДОСТУПА К ТЕРМИНАЛУ

Еще одним атрибутом терминалов, который вызван тем, что терминалы - это файлы, являются даты модификации, доступа и обновления. Каждый файл в системе имеет эти три даты, которые содержатся в его индексном дескрипторе (в секундах) в виде ДЛИННЫХ (long) чисел. Эти даты могут содержать некоторую интересную информацию. Как бы- ло отмечено при описании командного файла activ в предыдущей главе, дата модификации представляет собой последний момент времени, когда пользователь что-то набирал на своей клавиатуре. Другие даты тоже кое-что означают, но они не так часто используются.

    ОБРАБОТКА ТЕРМИНАЛОМ ВВОДИМОЙ ИНФОРМАЦИИ

Как уже обсуждалось ранее, по умолчанию драйвер терминала работа- ет в каноническом режиме, т.е. в режиме построчной обработки. Когда вы вводите символы, драйвер ожидает, пока вы нажмете возврат каретки, после чего передает для обработки всю строку. Если вы работаете не в каноническом режиме, то каждый символ передается для обработки не- посредственно после ввода. Наглядным примером такого режима работы яв- ляется редактор vi. Вы нажимаете по одной клавише для движения курсо- ра, удаления символов и т.д., поэтому редактор vi, очевидно, должен получать каждый символ сразу же, как только нажата клавиша. Каким образом это делается в программе? Прием старый и часто используется в UNIX, хотя и не очень хорошо описан в документации. Та- кого рода информацию можно добыть путем просмотра большого количества текстов программ. Необходимо отметить, что этот прием лучше всего ра- ботает в программах на языке Си. Командные файлы, написанные на языке shell, могут использовать для этой цели команду stty, но результат бу- дет не один и тот же. Следующий фрагмент программы на языке Си отклю- чает каноническую обработку, затем читает символы и выводит их на эк- ран. 1 #include 3 struct termio tsav, tchg; 5 main (argc, argv) 6 { 7 int c; 9 if (ioctl (0, TCGETA, &tsav) == -1) { 10 perror("can't get original settings"); невозможно получить исходные установки 11 exit(1); 12 } 14 tchg = tsav; 16 tchg.c_lflag &= ~(ICANON | ECHO); 17 tchg.c_cc[VMIN] = 1; 18 tchg.c_cc[VTIME] = 0; 20 if (ioctl (0, TCSETA, &tchg) == -1) { 21 perror("can't initiate new settings"); невозможно задать новые установки 22 } 24 while (1) 25 { 26 c = getchar(); 28 if (c == 'x') 29 break; 31 putchar(c); 32 } 34 if (ioctl(0, TCSETA, &tsav) == -1) { 35 perror("can't reset original settings"); невозможно вернуть исходные установки 36 exit(3); 37 } 38 } У нас есть две "терминальные" структуры данных, одна из которых содержит исходные установки, а другая - установки, которые мы изменяем и записываем. Первый системный вызов ioctl получает информацию об установках терминала. Затем мы присваиваем эти значения изменяемой структуре (строка 14). Модификации терминального интерфейса мы выпол- няем в строках 16-18. Строка 16 отключает каноническую обработку и эхо-отображение символов. Строка 17 устанавливает, что минимальное ко- личество нажатий на клавиши равно одному. Строка 18 определяет, что время ожидания для повторного чтения данных равно 0. По существу, это блочное чтение по одному символу. Новые значения терминальных характеристик устанавливаются в стро- ке 20. В этот момент режим работы терминала меняется. Цикл while чита- ет, проверяет и выводит символы. Только при вводе символа x цикл за- вершается, терминал устанавливается в первоначальное состояние, и программа заканчивает работу. Как мы уже заметили, операция чтения здесь является блочной. Это значит, что программа ожидает, пока вы введете символ. Если вы ничего не вводите, программа находится в бесконечном цикле ожидания. Каким образом мы изменяем режим чтения с блочного на посимвольный? Этот вопрос эквивалентен такому вопросу: "Как опросить клавиатуру в UNIX?". Опрос является весьма важным приемом для некоторых примене- ний. Опрос работает примерно так: "Посмотреть на клавиатуру. Если вве- ден символ, получить его и каким-то образом обработать. В противном случае продолжать делать то, что вы делали. После истечения интервала времени, определенного программой, проверить клавиатуру снова." Таким образом, если пользователь не нажимает на клавиши, программа продолжа- ет работу, а не ожидает, пока что-нибудь будет нажато на клавиатуре. Для выполнения такой работы нам нужно несколько более подробно рассмотреть терминальный интерфейс. Как было отмечено ранее, терминал представляет собой файл. Это значит, что он должен обладать всеми обычными свойствами файлов - возможностью открытия, закрытия, чтения, записи и т.д. Мы также видели, что терминалы имеют протокол работы, который может быть изменен командой stty. Мы видели, что для получения одного символа с клавиатуры используется протокол работы. Теперь мы увидим, что для выполнения опроса вы должны использовать технику, ко- торая относится к файлам, а не ioctl. Секрет здесь в том, чтобы открыть терминальный файл, изменяя ре- жим его открытия. Затем для получения одного символа используется тот же текст, что и в предыдущем случае - тем самым опрос достигнут. Вот текст программы: 1 #include 2 #include 4 struct termio tsav, tchg; 6 main (argc, argv) 7 { 8 int c; 10 /* change the terminal based on file primitives */ изменить режим терминала с помощью файловых примитивов 11 close(0); 12 if (open("/dev/tty",O_RDWR|O_NDELAY) == -1) { 13 perror("can't open tty"); невозможно открыть tty 14 exit(1); 15 } 17 /* change the terminal based on line disciplines */ изменить режим терминала с помощью протокола работы 18 if (ioctl (0, TCGETA, &tsav) == -1) { 19 perror("can't get original settings"); невозможно получить исходные установки 20 exit(2); 21 } 23 tchg = tsav; 25 tchg.c_lflag &= ~(ICANON | ECHO); 26 tchg.c_cc[VMIN] = 1; 27 tchg.c_cc[VTIME] = 0; 29 if (ioctl (0, TCSETA, &tchg) == -1) { 30 perror(can't initiate new settings"); невозможно задать новые установки 31 } 33 while (1) 34 { 35 putchar('.'); 36 c = getchar(); 38 if (c == 'x') 39 break; 41 putchar(c); 42 } 44 if (ioctl(0, TCSETA, &tsav) == -1) { 45 perror("can't reset original settings"); невозможно вернуть исходные установки 46 exit(3); 47 } 48 } Основное изменение производится в строках 11-15. Закрытие файла с нулевым дескриптором (который обозначает стандартное устройство ввода) закрывает стандартный ввод. Затем мы снова открываем файл /dev/tty. Значение дескриптора файла равно нулю, так что мы переназначили стан- дартный ввод на новое устройство. Фокус в том, что при открытии файла используется режим, называемый NODELAY. Это означает, что когда выпол- няется чтение по данному дескриптору файла (т.е. чтение стандартного ввода), вместо ожидания ввода выполняется просмотр, есть ли там что-нибудь, а затем работа продолжается. В бесконечном цикле строка 35 печатает точку. Когда вы запускаете эту программу, на экран выводится точка, как только программа попадает в цикл. Если вы ждете, то продолжают выводиться точки. Как только вы нажмете клавишу, выполнится эхо-отображение символа в промежутке между выводом точек. Это демонстрирует, что программа работает в то время, когда вы ничего не делаете.

    ВОЗМОЖНОСТИ ТЕРМИНАЛОВ

Теперь, когда мы имеем понятие о характеристиках терминальных ин- терфейсов, давайте перейдем к возможностям терминалов. ВОЗМОЖНОСТИ - это те функции, которые выполняет аппаратура терминала. Если мы знаем эту информацию, мы можем создать список возможных функций и использо- вать его, например, для работы редактора vi. Это осуществляется при помощи специального файла данных termcap (terminal capabilities - воз- можности терминала), который описывает возможности терминала. Большинство из существующих типов терминалов уже занесены в файл termcap. Это файл /etc/termcap. Файл termcap и редактор vi происходят из системы Berkeley. Такая комбинация оказалась настолько эффективной, что была перенесена в System V. В более поздней System V Release 3 файл termcap уже не используется, его заменяет файл terminfo фирмы AT&T. Мы применяли файл terminfo совместно с командным файлом today в главе 5, но подробное обсуждение terminfo выходит за пределы нашей книги. В системе Berkeley файл termcap по-прежнему остается стандар- том, и он заслуживает более детального рассмотрения. Имеется документация по termcap, но не думайте, что вы из нее много узнаете. В документации приводятся имена и однострочные описания поддерживаемых функций, но нет информации о том, как формировать из ничего записи этого файла. Самое лучшее, что мы можем посоветовать, это взять имеющуюся запись и изменить ее. В качестве примера мы приводим запись файла termcap для компьюте- ра Apple II. Это описание распространено в различных формах, но наш пример относится к видеоплате Videx UltraTerm для Apple II+. Заметим, что возможности, предоставляемые файлом termcap, являются обычно подм- ножеством тех возможностей, которые фактически предоставляет аппарату- ра. В частности, видеоплата в компьютере Apple выполняет некоторые функции, которые не умеет делать файл termcap, например комбинации настроечных битов для изменения видеоатрибутов. Самое большее, что мы можем сделать с видеоатрибутами посредством файла termcap, это вклю- чить или выключить инверсное отображение. С другой стороны, некоторые типы аппаратуры не обладают всеми возможностями, обеспечиваемыми файлом termcap. Например, одной из функций, которой недостает в Apple, является функция прокрутки ("scroll reverse"). Аппаратура не делает этого, поэтому и в termcap нет необходимости иметь описание этой функции. Вместо скроллинга (прокрутки) вниз, отображаемый на экране текст продолжает выводиться в верхней строке. Для того, чтобы получить представление о том, как termcap соот- носит общие характеристики терминала с конкретными возможностями, сравним терминалы Apple и vt52. Две соответствующие записи в termcap имеют много похожих функций, но совершенно разные коды для их выполне- ния. Приведем пример содержимого файла termcap: a2|aii|Apple II with UltraTerm :\ :bl=^G:\ :bs:\ :cd=^K:\ :ce=^]:\ :cl=^L:\ :cm=^^%r%+ %+ :\ :co#80:\ :cr=^M:\ :do=^J:\ :ho=^Y:\ :kb=^H:\ :kd=^J:\ :kl=^H:\ :kr=^\\:\ :ku=^_:\ :le=^H:\ :li#24:\ :nd=^\\:\ :nl=^J:\ :se=^O:\ :so=^N:\ :up=^_: В табл. 7-1 представлен список функций файла termcap с сопостав- лением терминалов Apple и vt52. Если какая-либо функция отсутствует у одного или другого терминала, это отмечается словом "нет". Таблица 7-1 Терминальные возможности и их конкретные значения
Функция Apple II vt52
bl - звуковой сигнал ^G ^G (bell) bs - возврат на шаг по коду ^H да да (can backspace with ^H) cd - очистка до конца экрана ^K \EJ (clear to end of display) ce - очистка до конца строки ^] \EK (clear to end of line) cl - очистка всего экрана ^L \EH\EJ (clear entire screen) cm - движение курсора ^^%r%+ %+ \EY%+ %+ (cursor motion) co - число позиций в строке #80 #80 (number of columns in a line) cr - возврат каретки ^M ^M (carriage return) do - сдвиг на строку вниз ^J ^J (down one line) ho - курсор в начало экрана (без команды cm) ^Y \EH (home cursor) kb - код клавиши backspace ^H ^H (sent by backspace key) kd - код клавиши "стрелка вниз" ^J \EB (sent by down arrow key) kl - код клавиши "стрелка влево" ^H \ED (sent by left arrow key) kr - код клавиши "стрелка вправо" ^\\ \EC (sent by right arrow key) ku - код клавиши "стрелка вверх" ^_ \EA (sent by up arrow key) le - курсор влево ^H ^H (cursor left) li - число строк экрана #24 #24 (number of lines per screen) nd - нестирающий пробел ^\\ \EC (nondestructive space) nl - символ перевода строки ^J ^J (newline character) pt - наличие аппаратной табуляции нет да (has hardware tabs) se - обычный экран ^O нет (end stand out mode (normal)) so - инверсный экран ^N нет (begin stand out mode (inverse)) sr - прокрутка нет \EI (scroll reverse) ta - символ табуляции ^I ^I (tab) up - сдвиг вверх на строку нет ^_ (up a line)
Самое интересное здесь, наверное, то, что терминалы vt52 и Apple имеют взаимно обратный порядок указания координат в команде движения курсора. Терминал vt52 воспринимает значения x и y в порядке YX, что является умолчанием для файла termcap. Apple воспринимает их в порядке XY, поэтому в записи файла termcap требуется поменять координаты местами, что указано обозначением %r в функции cm. Файл termcap позволяет вам спрятать основную информацию о специ- фических характеристиках терминала (за исключением характеристик, ко- торые могут отсутствовать у терминала, или специальных возможностей, которые не описаны в termcap). Это значит, что вы можете создавать терминально-независимые программы. При этом вам нет необходимости из- менять все специфические обращения к терминалу, такие как ESC-последо- вательности (символы, указывающие терминалу, что передаваемые после них символы (символ) должны интерпретироваться как управляющие коды). Это символы (\E) для терминала vt52 и (^) для Apple. Наилучший пример - способ использования файла termcap редактором vi. Он начинает выполнять указанную ему функцию, например движение курсора, после чего ставит вопрос: "Какой код функции, которую мы хо- тим выполнить?". Затем он ищет соответствующую последовательность в той информации, которую предоставляет termcap. С другой стороны, иногда вам необходимо оптимизировать какую-либо функцию по скорости, заставив ее посылать коды непосредственно на оп- ределенный терминал. В этом случае вам опять-таки полезен файл termcap, поскольку вы можете найти необходимую информацию в соот- ветствующем файле termcap, после чего закодировать эту информацию в вашей программе. Это мы и делаем в первой инструментальной программе данной главы - программе 'c'.
ИМЯ: c
c Быстрая очистка экрана

    НАЗНАЧЕНИЕ

Выводит последовательность символов очистки экрана с использова- нием быстрой программы на языке Си. Код очистки, указанный в тексте программы, следует изменить в соответствии с используемым терминалом.

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

c

    ПРИМЕР ВЫЗОВА

c Очистка экрана

    ТЕКСТ ПРОГРАММЫ

1 char id[] = "@(#) c v1.0 Fast clear screen Author: Russ Sage"; Быстрая очистка экрана 3 #define FF "\014" 5 main() 6 { 7 if (write(1, FF, 1) == -1) 8 write(2,"c: write error to stdout\n",25); ошибка записи в стандартный вывод 9 } ОПИСАНИЕ ЗАЧЕМ НАМ НУЖНА ПРОГРАММА c? В System V уже имеется команда для очистки экрана терминала - это команда clear. Она работает путем определения типа вашего терминала и затем вывода на экран символа очистки для данного терминала. Все прек- расно, но есть один существенный недостаток: она очень МЕДЛЕННАЯ! Мы же хотим как можно быстрее выполнить очистку экрана. Самой быстрой операцией ввода-вывода в системе является прямой системный вы- зов для чтения или записи. Мы применяем этот вызов, а также выполняем небольшую проверку ошибок для определения доступности стандартного устройства вывода. ЧТО ДЕЛАЕТ c? Программа 'c' выводит на экран символ очистки настолько быстро, насколько быстро может выполняться операция ввода-вывода в UNIX. При- меняя прямой системный вызов, мы избавляемся от необходимости за- пускать другую программу. Поэтому программа 'c' работает очень быстро. Мы уверены, что точно такую же функцию можно вызывать как команду Си-shell (поместить в csh alias), поэтому данная программа наиболее полезна тем, кто работает в System V. Для того чтобы определить, какой символ очистки соответствует ва- шему терминалу, найдите строку с обозначением cl в файле termcap. Это и есть то значение, которое вы должны вручную вставить в данную прог- рамму. Если вы работаете не на таком терминале, для которого эта прог- рамма написана, то данная команда будет работать неверно.

    ПОЯСНЕНИЯ

Первым делом мы должны найти в файле termcap код очистки экрана. Для терминала Apple это код ^L, а для vt52 это \EH\EJ. Как только вы найдете этот код, вставьте его в оператор define в строке 3 или сразу в оператор write в строке 7. В приведенном примере в качестве символа очистки экрана используется ^L. Наиболее быстрым способом передачи символа в файл является не- посредственное выполнение оператора write. Поскольку терминалы явля- ются файлами, мы можем выполнять запись непосредственно в них, пользу- ясь преимуществом предопределенных дескрипторов файла 0,1 и 2. Системный вызов write в строке 7 посылает символ очистки в файл с дескриптором 1, который является стандартным устройством вывода. Если операция записи неудачна (по ряду причин), то в файл с дескриптором 2, т.е. на стандартное устройство регистрации ошибок, выводится сообщение об ошибке. Здесь не проверяется, успешно ли завершилась запись на стандартное устройство регистрации ошибок. Если ошибка все-таки воз- никнет, то мы ее увидим. Программа не использует НИКАКИХ возможностей стандартного вво- да-вывода (stdio). НИКОГДА нельзя смешивать системные вызовы ввода-вы- вода (т.е. вызовы из раздела (2) документации по системным функциям, например read или write) со стандартными вызовами ввода-вывода (т.е. вызовами из раздела (3), такими как getchar и printf). Дополнительный буфер, который создается при выполнении функций stdio, не согласован во времени с системными вызовами, поэтому все выходные сообщения пере- мешиваются. Еще один аспект, о котором мы должны помнить, принимая решение об использовании системных вызовов, это преимущество получения как можно более короткого объектного кода. Небольшая программа загружается и ра- ботает быстрее. Для того, чтобы ненужные подпрограммы стандартного ввода-вывода не включались в наш объектный модуль, в исходном тексте программы не делается никаких ссылок на подпрограммы stdio. Тем не ме- нее, ваша система могла их каким-то образом включить. Так поступает XENIX, а вместе с stdio вызывается malloc и все остальное. Вы можете просмотреть таблицу символов вашего объектного модуля с помощью nm(1) или nlist(2). Вы увидите весь мусор, который был добавлен в ваш объек- тный модуль. Не так редко мы получаем 6 Кб кода всего лишь для одного оператора printf! Приучайтесь программировать непосредственно на ассемблере, чтобы достичь того, что вам нужно.

    ИССЛЕДОВАНИЯ

Когда эта программа была написана, возник вопрос: "Каким образом мы можем проверить неудачу записи на стандартное устройство вывода?". Раньше такой вопрос не стоял, но показалось, что неплохо было бы это сделать. Решение было найдено на страницах описания sh(1). Способ, ко- торым можно вызвать ошибку выполнения записи на стандартное устройство вывода, заключается в том, что нужно закрыть дескриптор файла стан- дартного устройства вывода. Это легко делается с помощью команды exec, которая является внутренней по отношению к shell: $ exec >&- Эта команда переназначает файловый дескриптор 1 стандартного вы- вода (обозначение >) на дескриптор файла (&) закрытого устройства (-). Такой эксперимент может оказаться полезным для более полной отладки ваших программ.

    ДИСКОВЫЕ УСТРОЙСТВА

К дисковым устройствам относятся гибкие и жесткие диски. Каждый диск может быть разделен на одну или несколько частей, каждая из кото- рых связана с файлом устройства. Основное отличие между дисками и терминалами заключается в том, что диски являются блочными устройствами, а терминалы - символьными. Вместо того, чтобы выполнять обмен информацией по одному символу, диски обмениваются блоками по 512 или 1024 символа. Имеются команды, которые управляют разбиением на блоки и буферизацией, что делает воз- можным выполнение блочных операций ввода-вывода.

    РАЗБИЕНИЕ ДИСКОВ НА РАЗДЕЛЫ

Части, или области диска, известны как разделы. Раздел может со- держать файловую систему, которая сгенерирована командой mkfs(1), или же может содержать неструктурированные данные, доступ к которым выпол- няется с помощью команды 'cpio -o'. В системе XENIX управление разделами осуществляется программой fdisk, которая концептуально подобна своей тезке в системе MS-DOS. В других системах UNIX используются другие имена. Например, в системе AT&T 7300 UNIX PC используется программа iv, что значит "format" (хо- тите верьте, хотите нет). Как упоминалось ранее, обычно разделы содер- жат одну файловую систему. В настоящее время в системах XENIX и SCO XENIX у вас есть возможность "разделить раздел" на более мелкие части для получения большего количества файловых систем. Это сделано по той причине, что машины с системами DOS и XENIX ограничены четырьмя диско- выми разделами, а у вас может возникнуть желание иметь больше файловых систем, чем число доступных разделов. В системе AT&T 7300 UNIX PC уп- равление разделами диска осуществляется по списку начальных номеров дорожек. Вы можете создать столько разделов, сколько хотите. Каждый компьютер имеет свои преимущества и недостатки. В каталоге /dev находятся имена как блочных устройств, так и сим- вольных. По этим именам вызываются различные драйверы устройств. Ниже приводится пример списка интерфейсов жестких дисков.
| | brw------- 1 sysinfo sysinfo 1, 0 Feb 18 17:07 /dev/hd00 | brw------- 1 sysinfo sysinfo 1, 15 Feb 18 16:59 /dev/hd01 | brw------- 1 sysinfo sysinfo 1, 23 Feb 18 16:59 /dev/hd02 | brw------- 1 sysinfo sysinfo 1, 31 Feb 18 16:59 /dev/hd03 | brw------- 1 sysinfo sysinfo 1, 39 Feb 18 16:59 /dev/hd04 | brw------- 1 sysinfo sysinfo 1, 47 Feb 18 17:07 /dev/hd0a | brw------- 1 sysinfo sysinfo 1, 55 Feb 18 17:09 /dev/hd0d | crw------- 1 sysinfo sysinfo 1, 0 Feb 18 16:59 /dev/rhd00 | crw------- 1 sysinfo sysinfo 1, 15 Feb 18 16:59 /dev/rhd01 | crw------- 1 sysinfo sysinfo 1, 23 Feb 18 16:59 /dev/rhd02 | crw------- 1 sysinfo sysinfo 1, 31 Feb 18 16:59 /dev/rhd03 | crw------- 1 sysinfo sysinfo 1, 39 Feb 18 16:59 /dev/rhd04 | crw------- 1 sysinfo sysinfo 1, 47 Feb 18 16:59 /dev/rhd0a | crw------- 1 sysinfo sysinfo 1, 55 Feb 18 17:09 /dev/rhd0d | Имена файлов с префиксом hd указывают блочные устройства, а с префиксом rhd - "неструктурированные" символьные устройства. Не все символьные устройства являются неструктурированными блочными уст- ройствами. Терминалы являются символьными устройствами, как мы уже ви- дели ранее в данной главе. В табл. 7-2 показаны различные характе- ристики этих двух типов устройств. Таблица 7-2 Сравнение блочных и символьных устройств
Блочное устройство Символьное устройство
/dev/hd0, /dev/fd0 /dev/rhd0, /dev/rfd0 буфер управляется ядром буферизация отсутствует, системы, медленное устройство быстрое устройство произвольное размещение последовательное размещение блоков данных блоков данных доступ через файловую доступ непосредственно систему на диск cpio -p cpio -o, -i mkfs, mount, df, du tar fsck, fsdb
Как видите, существует много способов работы с устройствами. Давайте рассмотрим устройство /dev/hd01 из приведенного выше списка. Если вы хотите адресоваться к физическому разделу на диске как к блочному устройству, вы можете создать на нем файловую систему. Для этого вам нужно выполнить следующую команду, которая создаст файловую систему размером 5000 Кб (5 Мб) на жестком диске: # mkfs /dev/hd01 5000 Внутри раздела (размером не менее 5000 Кб) размещается файловая система. Файловая система содержит суперблок, списки свободных блоков и т.п., то есть все, что необходимо для хранения файлов, которые раз- мещаются здесь. Однако, создание файловой системы совсем НЕ означает, что вы сразу же можете получить к ней доступ. Сначала вам необходимо смонтировать файловую систему. Команда для выполнения этой операции может иметь такой вид: # mount /dev/hd01 /mount_pt Файлы могут быть помещены в дисковый раздел командами mv или cp, путем переадресации вывода в каталог с этим именем, например, >/mount_pt/file. Для использования раздела диска в качестве области неструктуриро- ванных данных, а не блочного устройства, применяйте файл с именем сим- вольного устройства, которое начинается с буквы r. Например, для использования того же устройства, что и в предыдущем примере, в ка- честве неструктурированного устройства, укажите имя /dev/rhd01. (Из списка устройств вы видите, что это символьное устройство, так как права доступа в первой колонке начинаются с символов crw, а не brw). Это устройство (и соответствующий раздел) в данный момент не имеет файловой системы и является просто набором байтов. Единственным огра- ничением является то, что вы можете записать в этот раздел не более 5 Мб данных. Вот пример команды, использующей неструктурированное устройство: $ find . -print | cpio -ocBv > /dev/rhd01

    ИЗУЧЕНИЕ ДАННЫХ

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

    ОБНОВЛЕНИЕ ФАЙЛОВОЙ СИСТЕМЫ

Мы описали суперблок как запись с ключевой информацией о размере и содержимом файловой системы. Причиной разрушения файловой системы обычно являются проблемы, возникающие в суперблоке. Команда sync(1) выполняет запись образа суперблока на диск, тем самым обновляя его. Иногда эта операция должна выполняться автоматически и постоянно для того, чтобы образы суперблока на диске и в памяти были одинаковы. В настоящее время в System V включена программа update, которая запуска- ется из загрузочного файла /etc/rc. Она живет в системе и исполняет команды sync и sleep. В результате информация о состоянии файловой системы на диске хранится со всеми текущими изменениями, произведенны- ми с самой файловой системой. Если у вас нет такой программы, вы може- те написать командный файл на языке shell, которая работает в цикле, вызывая команду sync через соответствующие интервалы команды sleep. Запустите этот командный файл в фоновом режиме, чтобы поддерживать це- лостность файловой системы.

    МОНТИРОВАНИЕ ФАЙЛОВЫХ СИСТЕМ

Давайте рассмотрим, что происходит, когда файловая система монти- руется в древовидной структуре системы. На рис. 7 -3 показано, как взаимодействуют между собой индексные дескрипторы (inodes) двух файло- вых систем. Рис. 7-3 Монтирование одной файловой системы в другую
+------------------------------+ |Раздел| Физическое| Логическое| |диска | имя | имя | |------------------------------| | 1 | /dev/hd01 |/dev/root | | 2 | /dev/hd02 |/dev/usr | +------------------------------+ +----+ | | Раздел 1 | / |inode 2 (ls -lia /) / / | | \ \ \ / / +----+ \ \ \ / / / | \ \ \ \ / / / | \ \ \ \ / / / | \ \ \ \ +---+ +---+ +---+ +---+ +-----+ +---+ +---+ +---+ |bin| |dev| |etc| |lib| |lost+| |mnt| |tmp| |usr| inode 245 | | | | | | | | |found| | | | | | +---+ +---+ +---+ +---+ +---+ +-----+ +---+ +---+ +-| | / | \ / | \ / |\ / | \ / | | \ | | / | \ +---+ /|\ /|\ /|\ /|\ | /----------------+ / КОМАНДА / # /etc/mount /dev/hd02 /usr / +-----+ | | Раздел 2 | / | inode 2 (ls -lia /usr) | | +-----+ \ \ \ / / / | \ \ \ \ \ / / / | \ \ \ \ \ / / / | \ \ \ \ \ / / / | \ \ \ \ \ / / / | \ \ \ \ \ +---+ +---+ +-------+ +---+ +-----+ +-----+ +-----+ +---+ +---+ |adm| |bin| |include| |lib| |lost+| |pre- | |spool| |sys| |tmp| | | | | | | | | |found| |serve| | | | | | | +---+ +---+ +-------+ +---+ +-----+ +-----+ +-----+ +---+ +---+ / | \ / | \ / | | \ / | \ /|\ / || \ / | \ / / | \ / | \ \ / / | \ / | \ \ /|\ /|\ /|\ /|\ / | \ /|\ /|\ /|\
В примере, показанном на рис. 7-3, файловая система из раздела 2 монтируется в корневой файловой системе (раздел 1) в каталог /usr. Од- нако мы помним, что каждая файловая система имеет свой собственный корневой каталог. В каждой файловой системе нумерация индексных деск- рипторов файла начинается с числа 2, поэтому номера индексных дескрип- торов дублируются в двух файловых системах. Это и является причиной, по которой не могут быть образованы связи между файлами, находящимися в разных файловых си бразованы связи между файлами, находящимися Одним из атрибутов корневого каталога является то, что номер его индексного дескриптора равен 2. Это значение может быть проверено в корневом каталоге командой "ls -lid /". Каталог /usr - это просто еще один файл (а именно каталог) в корневой файловой системе. Этот каталог может содержать файлы и подчиненные каталоги, которые хранятся в раз- деле 1. После выполнения команды "mount /dev/hd02 /usr" корневой ката- лог раздела 2 (индексный дескриптор 2) помещается в каталог /usr (ин- дексный дескриптор 245). Если какие-либо файлы существуют в каталоге /usr в разделе 1, они остаются там, но получить доступ к ним вы не мо- жете. Единственным способом увидеть их является размонтирование файло- вой системы, которая была смонтирована на их место. Хитрость команды mount заключается в том, что она представляет новый раздел как бы при- надлежащим реальному корневому разделу. В сущности, это позволяет иметь безграничную файловую систему. Механизмом, который позволяет производить это, является таблица смонтированных устройств, находящаяся внутри ядра системы. Когда вы- полняется обращение к файлу, его индексный дескриптор определяет марш- рут, по которому находится данный файл. Если в таблице смонтированных устройств имеется запись, то этот маршрут ведет на другой раздел диска или в другую файловую систему. Для того чтобы убедиться, что вновь смонтированная файловая система уникальна, посмотрите индексный деск- риптор каталога /usr сначала из корневого каталога (командой "ls -li /", индексный дескриптор 245), а затем из другой файловой системы (ls -ldi /usr, индексный дескриптор 2).

    КАК ПОЛУЧИТЬ ПОБОЛЬШЕ ИНФОРМАЦИИ О ФАЙЛОВОЙ СИСТЕМЕ?

Как указывалось ранее, файловая система размещается внутри разде- ла на диске. Файловые системы создаются командой mkfs(1), поддержива- ются командой fsck(1), отлаживаются командой fsdb(1), а первый доступ к ним осуществляется командой mount(1). Каталог /usr/include содержит все включаемые файлы для использования в программах на языке Си, реа- лизующих эти команды. Таким образом, этот каталог представляет собой прекрасную возможность для поиска информации о файловой системе, поскольку включаемые файлы содержат глобальные определения, используе- мые подпрограммами файловой системы. В документации Bell Labs (в руко- водстве программиста) также описаны некоторые внутренние таблицы, используемые файловой системой. Теперь мы готовы рассмотреть программные средства для автоматиза- ции рутинной работы с файловой системой.
ИМЯ: mntf
mntf Монтирование и размонтирование гибкого диска

    НАЗНАЧЕНИЕ

Монтирует и размонтирует устройство гибкого диска в каталоге как файловую систему с возможностью записи/чтения или только чтения.

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

mntf [-d] [-h] [-l] [-r] [-s] Опции: -d размонтирование гибкого диска из корневой файловой системы -h использование устройства с высокой плотностью записи (а не с низкой) -1 использование устройства 1, а не устройства 0 -r монтирование гибкого устройства как файловой системы с возможностью только чтения -s использование имен устройств, принятых в System V По умолчанию выполняется монтирование гибкого диска 0 в каталог /mnt.

    ПРИМЕР ВЫЗОВА

mntf -d -1 Размонтирование гибкого диска на устройстве 1.

    ТЕКСТ ПРОГРАММЫ

1 : 2 # @(#) mntf v1.0 Mount floppies Author: Russ Sage Монтирование гибких дисков 4 CMD="/etc/mount" 5 DIR="/mnt" 6 DRIVE="0" 7 DENSITY="48ds9" 8 SYSTEM="xenix" 10 if [ $# -gt 0 ] 11 then for ARG in $* 12 do 13 case $ARG in 14 -d) CMD="/etc/umount" 15 DIR="";; 16 -h) DENSITY="96ds15";; 17 -1) DRIVE="1" 18 if [ -d /mnt1 ] 19 then DIR="/mnt1" 20 else echo "the directory /mnt1 does not exist" >&2 нет каталога /mnt1 21 echo "using the directory /mnt instead" >&2 используется каталог /mnt 22 fi;; 23 -r) DIR="$DIR -r";; 24 -s) SYSTEM="sysv";; 25 *) echo "mntf: invalid argument $ARG" >&2 26 echo "usage: mntf [-d] [-h] [-1] [-r] [-s]" >&2 27 echo " -d dismount" >&2 28 echo " -h high density" >&2 29 echo " -1 use drive 1" >&2 30 echo " -r read only" >&2 31 echo " -s System V device" >&2 32 echo " default: mount XENIX drive 0 48 tpi to " >&2 33 echo " /mnt as a read/write filesystem" >&2 34 exit 1;; 35 esac 36 done 37 fi 39 case $SYSTEM in 40 sysv) $CMD /dev/fp${DRIVE}21 $DIR;; 41 xenix) $CMD /dev/fd${DRIVE}${DENSITY} $DIR;; 42 esac

    ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ

CMD Основная команда, подлежащая выполнению DIR Каталог, в котором производится монтирование устройства DENSITY Плотность записи в виде, указанном в имени устройства DRIVE Номер устройства, начиная с 0 SYSTEM Тип имени устройства, принятый в UNIX'е ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ mntf? В машинах с гибким диском это устройство часто используется в повседневных операциях. Оно применяется в качестве источника при инсталляции системы и как обычный носитель для операций копирования. Гибкие диски можно использовать в системе UNIX двумя способами. Первый является неструктурированной последовательностью байтов, что полезно для копирования магнитных лент и хранения архивов. Второй способ - поблочный, ориентирован на поддержку файловой структуры. Для второго способа существует мощная поддержка на уровне файловой систе- мы, но некоторые функции мы должны реализовать самостоятельно. Для того чтобы использовать гибкий диск как файловую систему в UNIX, вам необходимо подготовить диск и смонтировать его как файловую систему. Когда вы закончите работу, вы должны размонтировать гибкий диск. Это отличается от системы DOS, в которой гибкие диски можно вставлять и вынимать когда угодно, если только в этот момент на них не идет запись. Поскольку использование гибких дисков включает в себя взаимосвя- занные шаги монтирования и размонтирования, то было бы вполне естест- венным применять одну команду с соответствующими опциями для выполне- ния монтирования и размонтирования. Однако UNIX так не делает. Наш ко- мандный файл mntf объединяет эти две функции в одной команде для упро- щения работы с гибким диском. Для того чтобы сделать нашу программу более независимой, мы предусмотрели в ней поддержку устройств системы XENIX наравне с устройствами System V. (Системы Berkeley (BSD) не так часто используют гибкие диски, поэтому мы не пытались иметь с ними де- ло.) ЧТО ДЕЛАЕТ mntf? Эта программа обеспечивает поддержку всех возможностей для монти- рования и размонтирования гибких дисков. Она предоставляет все опции, необходимые команде mount, акцентирует внимание на тех аспектах файло- вой системы, которые относятся к гибким дискам, и уменьшает количество нажатий на клавиши, необходимых для выполнения этой работы. Действие программы по умолчанию заключается в монтировании гибко- го диска низкой плотности записи, находящегося в устройстве 0, в ката- лог /mnt. Имеется много опций, чтобы попросить программу mntf сделать то, что вам нужно. Опция -h поддерживает диск высокой плотности (1.2 Мб). В машинах PC AT первое из устройств гибких дисков имеет 96 доро- жек на дюйм, объем 1.2 мегабайта, но может также читать и писать гиб- кие диски с более низкой плотностью. Второй гибкий диск является уст- ройством низкой плотности с 48 дорожками на дюйм и объемом 360 кило- байт. Опция -1 (цифра один, а не буква l) выполняет монтирование гибко- го диска в устройстве 1, а не 0. Опция -r монтирует файловую систему с возможностью ТОЛЬКО ЧТЕНИЯ. Для РАЗМОНТИРОВАНИЯ диска вместо монтиро- вания используется опция -d. Если применяется опция -s, имя устройства изменяется таким образом, чтобы оно соответствовало системе System V, а не XENIX. Это незначительная проблема, поскольку схемы именования не очень отличаются. Данная программа создана для системы XENIX и обеспе- чивает наилучшие возможности именно в ней. Не все опции совместимы друг с другом, но проверка на совмести- мость не выполняется. Например, команда "mntf -d -r" пытается размон- тировать файловую систему с возможностью только чтения, а команда UNIX unmount, которая выполняет эту операцию, отбрасывает ее, выдавая сооб- щение об ошибке. В целях упрощения мы отказались от проверки соот- ветствия опций, а вместо этого предоставили UNIX'у право выдавать сообщения об ошибках для информирования пользователя о возникших проб- лемах. Если вы хотите, чтобы эту программу мог применять относительно неопытный пользователь, вам нужно вставить в нее выполнение таких про- верок.

    ПРИМЕРЫ

1. $ mntf -s Монтирование гибкого диска как файловой системы с возможностью записи-чтения и с использованием имен устройств, принятых в System V. 2. $ mntf -h -1 -r Монтирование гибкого диска высокой плотности записи на устройстве 1 как файловой системы с возможностью только чтения и с использованием формата имен устройств, принятого в XENIX. Эта команда должна закон- читься неудачей (устройство 1 имеет низкую плотность). 3. $ mntf -d -h Размонтирование файловой системы на устройстве 0 с высокой плот- ностью записи и с использованием имен устройств, принятых в XENIX.

    ПОЯСНЕНИЯ

Для того чтобы максимально упростить программу, все фактически выполняемые команды помещены в текстовые строки. Это позволяет достичь большей гибкости при написании программы. Результатом анализа команд- ной строки является формирование команды, которая выполняется в конце программы mntf. В строках 4-8 инициализируются установки по умолчанию. Переменная CMD содержит команду UNIX, которая в итоге должна быть выполнена, по умолчанию это команда mount. Переменная DIR указывает каталог, в кото- рый должно быть смонтировано устройство, по умолчанию это каталог /mnt. Переменная DRIVE является номером устройства (по умолчанию 0) и используется для формирования корректного имени устройства. Переменная DENSITY по умолчанию установлена для носителя низкой плотности, т.е. 48 дорожек на дюйм, двусторонняя дискета с 9 секторами на дорожку (48ds9). В строке 10 проверяется, указаны ли в командной строке какие-либо аргументы. Если количество аргументов больше нуля, последовательно проверяется каждый аргумент. Если какой-либо из аргументов соот- ветствует образцам в строках 13-35, то он изменяет содержимое команд- ной строки. Строка 14 управляет опцией -d для размонтирования гибкого диска. Переменная CMD изменяется на umount вместо mount. После этого перемен- ной DIR присваивается нулевое значение, поскольку команде umount тре- буется не каталог, а только имя устройства. Переменная DIR должна быть частью строки с командой для того, чтобы мы могли использовать одну и ту же "заготовленную" командную строку для всех вариантов. В данном случае мы устанавливаем эту переменную в нуль, а shell при синтакси- ческом разборе удаляет ее из командной строки. В строке 16 выполняется изменение плотности записи используемого носителя. Обращение к различным типам носителей выполняется по именам файлов устройств. Каждое имя указывает драйвер устройства, который ра- ботает с соответствующей аппаратурой. Устройство высокой плотности мо- жет работать в режимах как высокой, так и низкой плотности записи. Од- нако если вы укажете имя устройства с высокой плотностью записи, а на самом деле оно имеет низкую плотность, то драйвер работать не будет из-за ошибок чтения.

    ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ ОБ ИМЕНАХ УСТРОЙСТВ

Ниже приводится список, полученный командой ls в каталоге dev для машины XT, в которой нет устройств высокой плотности записи. Этот список позволяет проиллюстрировать, каким образом осуществляется обра- щение к именам устройств:
| | 32 brw-rw-rw- 3 bin bin 2, 4 Jun 25 09:25 /dev/fd0 | 32 brw-rw-rw- 3 bin bin 2, 4 Jun 25 09:25 /dev/fd048 | 126 brw-rw-rw- 1 root root 2, 12 Feb 18 17:09 /dev/fd048ds8 | 32 brw-rw-rw- 3 bin bin 2, 4 Jun 25 09:25 /dev/fd048ds9 | 125 brw-rw-rw- 1 root root 2, 8 Feb 18 17:09 /dev/fd048ss8 | 127 brw-rw-rw- 1 root root 2, 0 Feb 18 17:09 /dev/fd048ss9 | 131 brw-rw-rw- 3 root root 2, 5 Feb 18 17:09 /dev/fd1 | 131 brw-rw-rw- 3 root root 2, 5 Feb 18 17:09 /dev/fd148 | 129 brw-rw-rw- 1 root root 2, 13 Feb 18 17:09 /dev/fd148ds8 | 131 brw-rw-rw- 3 root root 2, 5 Feb 18 17:09 /dev/fd148ds9 | 128 brw-rw-rw- 1 root root 2, 9 Feb 18 17:09 /dev/fd148ss8 | 130 brw-rw-rw- 1 root root 2, 1 Feb 18 17:09 /dev/fd148ss9 | Крайнее слева число представляет собой номер индексного дескрип- тора. Мы используем его как ссылку для определения уникального имени файла. Как мы уже отмечали ранее, несколько имен устройств могут от- носиться к одному и тому же файлу, рассматриваемому с различных точек зрения. Например, в данном списке вы видите, что три устройства имеют индексный дескриптор 32. Второе число слева представляет собой коли- чество связей. Когда оно больше единицы, то это также указывает, что несколько устройств являются на самом деле одним файлом, а следова- тельно используют один и тот же индексный дескриптор. Следующие два числа являются старшим и младшим номером. Старший номер относится к драйверу устройства, а младший является уникальным номером одного из устройств, управляемых одним и тем же драйвером. Большинство из этих имен устройств соответствуют определенному шаблону. Они состоят из символов fd (floppy disk - гибкий диск), цифры 0 или 1 (номер устройства), числа 48 (плотность, выраженная в виде ко- личества дорожек на дюйм), символов ss или ds (single-sided - односто- ронняя или double-sided - двусторонняя дискета) и цифры 8 или 9 (число секторов). Мы видим по индексным дескрипторам, что устройство fd0 связано с устройствами fd048 и fd048ds9. Самым информативным именем (и самым трудным при вводе с клавиатуры) является имя fd048ds9. Оно точно выра- жает, к какому устройству и типу носителя мы обращаемся. Для того что- бы упростить указание этого имени, устройство fd048ds9 связывается с более короткими именами. Все три имени файла являются корректными. Следующий список получен на машине AT, в которой имеется уст- ройство высокой плотности:
| | 102 brw-rw-rw- 3 bin bin 2, 7 Jun 17 14:28 /dev/fd0 | 95 br--r--r-- 2 bin bin 2, 3 Jun 6 09:23 /dev/fd048 | 93 br--r--r-- 1 bin bin 2, 2 Jun 6 09:23 /dev/fd048ds8 | 95 br--r--r-- 2 bin bin 2, 3 Jun 6 09:23 /dev/fd048ds9 | 92 br--r--r-- 1 bin bin 2, 0 Jun 6 09:23 /dev/fd048ss8 | 94 br--r--r-- 1 bin bin 2, 1 Jun 6 09:23 /dev/fd048ss9 | 102 brw-rw-rw- 3 bin bin 2, 7 Jun 17 14:28 /dev/fd096 | 102 brw-rw-rw- 3 bin bin 2, 7 Jun 17 14:28 /dev/fd096ds15 | 99 brw-rw-rw- 3 bin bin 2, 11 Jun 26 19:34 /dev/fd1 | 99 brw-rw-rw- 3 bin bin 2, 11 Jun 26 19:34 /dev/fd148 | 97 br--r--r-- 1 bin bin 2, 10 Jun 6 09:23 /dev/fd148ds8 | 99 brw-rw-rw- 3 bin bin 2, 11 Jun 26 19:34 /dev/fd148ds9 | 96 br--r--r-- 1 bin bin 2, 8 Jun 6 09:23 /dev/fd148ss8 | 98 br--r--r-- 1 bin bin 2, 9 Jun 6 09:23 /dev/fd148ss9 | 103 brw-rw-rw- 2 bin bin 2, 15 Jun 6 09:23 /dev/fd196 | 103 brw-rw-rw- 2 bin bin 2, 15 Jun 6 09:23 /dev/fd196ds15 | Если мы посмотрим на записи с индексным дескриптором 102, начиная с середины списка, то увидим прогрессирующее упрощение имен по мере продвижения к первой записи - устройству 0, которое имеет высокую плотность записи по умолчанию. Для того чтобы обратиться к нему как к устройству с низкой плотностью записи, необходимо использовать имя fd048, а не fd0. Поскольку большинство используемых гибких дисков име- ют низкую плотность записи, то имя fd048ds9 является умолчанием в программе mntf. Строка 17 соответствует опции -1 для указания устройства 1 вместо устройства 0. Строки 18-22 проверяют, сиществует ли каталог для монти- рования второго устройства. Если вы используете два гибких диска од- новременно, то вы не можете монтировать их оба в один и тот же ката- лог. Для разрешения этой проблемы программа mntf использует для монти- рования устройства 1 каталог /mnt1, а не /mnt. Если каталог /mnt1 не существует, по умолчанию используется каталог /mnt, и все хорошо, если вы используете только устройство 1. Однако следует избегать монтирова- ния одного гибкого диска на место второго. Вы можете получить непред- виденные результаты. Если вы собираетесь монтировать два гибких диска, убедитесь, что у вас есть и каталог /mnt, и /mnt1. Строка 23 делает монтируемую файловую систему доступной только для чтения в случае, если была указана опция -r, что выполняется до- бавлением символов -r к имени каталога. Это не является частью имени каталога, но когда shell выполняет обработку команды, пробела между именем каталога и -r достаточно, чтобы распознать -r как опцию. Строка 24 соответствует опции -s и присваивает переменной SYSTEM значение sysv. Это означает, что нужно использовать другие соглашения об именах устройств. Строки 25-34 выполняют проверку на ошибки в командной строке. Лю- бая опция, отличная от уже проверенных, является ошибкой, поэтому все, что соответствует улавливающей ветке оператора case (*), считается не- допустимой опцией. В этом случае выводится синтаксическая подсказка, и программа завершается. В строках 39-42 выполняется вся основная работа. Оператор case действует в соответствии со значением переменной SYSTEM. Если оно рав- но "sysv", выполняется строка 40. В противном случае выполняется стро- ка 41 для системы XENIX. Обратите внимание, что в нашей версии команд- ного файла mntf в строке sysv имеется только переменная с номером уст- ройства. Если вы используете System V, вы можете добавить переменную для указания плотности записи или другие параметры, которые вам нужны. Строка 41 выполняет версию команды, рассчитанную на систему XENIX. Переменная CMD содержит, как мы отмечали, команду монтирования (mount) или размонтирования (umount). Последовательность символов /dev/fd указывает файл устройства для гибкого диска. Переменная DRIVE равна 0 или 1. Переменная DENSITY указывает устройство с высокой или низкой плотностью записи. Если должна быть выполнена команда монтиро- вания, переменная DIR содержит каталог. Если выполняется размонтирова- ние, значение переменной DIR равно нулю.

    ЗАМЕЧАНИЕ ПО ВОПРОСУ БЕЗОПАСНОСТИ

Обычно только суперпользователь (root) может монтировать файловую систему. В больших системах это имеет смысл. Однако на небольших настольных машинах это может быть слишком ограничивающим фактором. Для того чтобы обойти это требование, используйте возможность изменения прав доступа. Чтобы позволить любому пользователю выполнять команды монтирования и размонтирования, примените следующие команды: # chown root /etc/mount <- делает пользователя root владельцем модуля /etc/mount # chmod 4511 /etc/mount и дает возможность выполнять команду mount всем пользователям # chown root /etc/umount <- делает то же самое для команды # chmod 4511 /etc/umount размонтирования Эти команды облегчают всем пользователям работу с гибким диском, но одновременно открывают огромную дыру в защите системы. Если кто-ли- бо уже проложил тропинку прав доступа на гибкий диск (см. главу 9), то монтирование файловой системы продолжит эту тропинку в главную систему и позволит такому пользователю стать суперпользователем всей системы в целом просто с гибкого диска!
ИМЯ: mntlook
mntlook Поиск файловых систем на устройствах

    НАЗНАЧЕНИЕ

Просмотр всех файлов дисковых устройств и обнаружение всех файло- вых систем, включая немонтированные.

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

mntlook

    ПРИМЕР ВЫЗОВА

mntlook /dev/hd* Поиск файловых систем на всех жестких дисках

    ТЕКСТ ПРОГРАММЫ

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

    ПРИМЕРЫ

1. $ mntlook /dev/hd13 Поиск суперблока на устройстве с именем hd13. Это имя указывает устройство 1, третий раздел. Для просмотра разделов в среде XENIX нуж- но запустить программу fdisk. Для System V нужно воспользоваться ко- мандой iv. 2. $ mntlook /dev/fd0* Поиск файловых систем на гибких дисках с любой плотностью записи, находящихся в устройстве 0. Это снова пример для системы XENIX. 3. $ for DEV in /dev/*[fh]d* > do > echo "checking device: $DEV" > mntlook $DEV > done Данный цикл выполняется по всем именам устройств для гибих и жестких дисков по очереди. Каждое имя выводится на экран. Затем прове- ряется, содержит ли данное устройство файловую систему.

    ПОЯСНЕНИЯ

Строки 3-7 определяют включаемые файлы, которые использует данная программа. Вам необходимо изучить эти файлы, поскольку они не только помогут вам понять работу этой программы, но и покажут вам некоторые важные значения, имеющие отношение к файловым системам. Строка 9 определяет размер используемого буфера. Этот буфер при- меняется только для хранения сообщений об ошибках, поэтому он не дол- жен быть очень большим. Строка 15 определяет структуру суперблока. Он имеет тип filesys (см. включаемый файл sys/types.h). На моей машине суперблок имеет раз- мер 1024 байта. Если вы не знаете точно размер буфера у вас, то вы мо- жете проверить его, вставив в программу следующий оператор: printf ("the size is %d",sizeof(sb)) Строка 16 описывает рабочую переменную d и дескриптор файла dev. Строка 17 объявляет буфер размером BSIZE. Строки 19-59 - это один большой цикл for. Этот цикл представляет собой всю остальную часть программы. Он выполняется столько раз, сколько аргументов указано в командной строке. Счетчик цикла начина- ется с 1, поскольку первым аргументом argv[0] является имя команды. В качестве аргументов должны быть указаны имена файлов, поэтому данный цикл использует каждое имя по очереди. В строках 21-26 проверяется, не начинается ли текущий рассматри- ваемый нами аргумент со знака '-'. Если да, то это опция, что представляет собой ошибку, поэтому выводится сообщение об ошибке и оператор continue вызывает выполнение следующей итерации цикла. Таким образом, данная программа отвергает опции, но работает при обнаружении имен файлов. Считая, что имя файла было найдено, строки 27-32 открывают файл устройства с этим именем только для чтения. Если данный вызов open не- удачен, мы посылаем сообщение в буфер вместе с именем, указанным в ко- мандной строке. Этот буфер передается программе обработки ошибок (системной), которая использует наше сообщение как первую часть своего сообщения об ошибке. Она выводит системное сообщение, которое опреде- ляет данную ошибку. По оператору continue начинается выполнение следу- ющей итерации цикла for. Строки 35-39 читают первый блок файла. Для корневой файловой системы первым блоком является загрузочная запись. Если при этом чте- нии возникает ошибка, выводится сообщение об ошибке и выполнение цикла продолжается. Строки 42-46 читают второй блок, который должен находиться в том месте, где размещается суперблок, если он имеется. По информации, про- читанной и помещенной в структуру, мы можем получить доступ к каждому элементу по его имени. Строка 48 проверяет, равно ли магическое число в структуре маги- ческому числу, определенному в файле заголовка. Если они совпадают, программа mntlook выводит имя файла устройства и сообщение о том, что файловая система корректна, имя файловой системы (если оно имеется), имя пакета дисков, размер используемого блока и магическое число в шестнадцатиричном виде. В строках 53-54 мы имеем подобие кодированной структуры: оператор printf использует структуру типа if-then-else для указания строки, ко- торую нужно выводить. После того как выведена первая строка, выполня- ется проверка и если она прошла успешно, то выводится значение 512. Если в результате проверки получено значение "ложь", выводится значе- ние 1024. Этот метод описан в книге B.W.Kernighan, D.M.Ritchie "The C Programming Language" (Prentice-Hall, 1978). Строка 58 закрывает устройство, и цикл возвращается в начало для принятия следующего имени устройства.

    РАЗМЕРНЫЕ ПАРАМЕТРЫ

Теперь, когда мы рассмотрели взаимоотношения между устройствами и файловыми системами и некоторые параметры, связанные с форматами дисков, давайте обратимся к гайкам и болтикам этих устройств. Хотя основная часть этой информации может показаться экзотичной, она может оказаться важной при определенных обстоятельствах. Например, для уста- новки системы UNIX на новую машину вам нужно разбить диск на сегменты и понимать, каким образом UNIX фактически располагается на диске. Если вы создаете программы, которые выполняют какую-либо операцию низкого уровня с диском, вам, очевидно, необходимо понимать, что вы делаете. Администраторам, поскольку они должны добавлять новые устройства в систему, необходимо уметь определять количество файловых систем (т.е. сколько можно создать разделов на диске), их размеры и знать, каким образом установить файловые системы в эти разделы. Администраторы должны также уметь писать или модифицировать драйверы устройств. Нако- нец, при работе с дисками возникают проблемы, такие как плохие блоки, которые необходимо изолировать и с которыми приходится иметь дело.

    РАЗМЕРЫ БЛОКОВ

System V является последним достижением ветви фирмы AT&T в фа- мильном дереве UNIX. Это означает, что System V содержит последние правки, внесенные в исходную систему UNIX. Эти правки предназначены для того, чтобы сделать UNIX жизнеспособным и стойким коммерческим продуктом. Для повышения устойчивости были внесены изменения, касающи- еся работы с файлами и размеров их блоков. Обычно обмен данными с дисками осуществляется блоками по 512 бай- тов. Дисковая аппаратура имеет дело именно с таким размером. Для учета этого факта UNIX первоначально использовал 512-байтные блоки внутри файловой системы, что, возможно, облегчало написание программ и созда- вало впечатление, что так и нужно. Однако нельзя отрицать, что при этом UNIX может работать медленно! Для ускорения работы внутренние программы в настоящее время используют блоки размером 1024 байта. Сам диск должен выполнить два обращения к 512-байтным блокам, но в системе две эти операции чтения рассматриваются как одна операция чтения блока размером 1024 байта. Единственная проблема заключается в том, что одни утилиты выдают ре- зультаты в 512-байтных блоках, а другие - в 1024-байтных, в зависи- мости от того, когда они были написаны. Когда сильно приближаются пре- делы свободного пространства на диске, вам действительно нужно знать, с каким размером вы имеете дело. Для лучшего понимания проблемы размеров блоков, в табл. 7-3 пока- зано, какие утилиты какой размер блока используют. Эта информация по- лучена в основном из System V на машине VAX, из другого варианта System V и из XENIX. Эти значения могут отличаться на разных машинах, но идея сохраняется. Вы видите, что большинство утилит выдают результат в блоках раз- мером 512 байтов, но утилиты, относящиеся к файловой системе, выдают результат в 1024-байтных блоках. Поскольку UNIX обращается к дисковому пространству поблочно, важно уметь точно вычислять, сколько свободного пространства в файловой системе. Весьма плоха ситуация, когда имеется какой-то большой файл в редакторе vi (который использует файл /tmp для промежуточного редактирования), а на диске недостаточно места для за- писи временного файла редактора vi в реальный файл на диске. На самом деле это может случиться на персональных машинах с ограниченным (ска- жем, 20 Мбайт) объемом жесткого диска. Таблица 7-3 Размеры блоков для различных команд системы UNIX
512 байтов/блок 1024 байта/блок
ls -s fdisk (размеры разделов) sum mkfs cpio fsck df du

    РАСЧЕТЫ, СВЯЗАННЫЕ С БЛОКАМИ

Еще одним важным вопросом, имеющим отношение к физическим уст- ройствам и логическим файловым системам, является определение местона- хождения определенного блока на жестком диске. Номер этого блока вы- числяется по номерам цилиндра, дорожки и сектора. Знание номеров блоков становится важным, когда на диске появля- ется дефектное место. Это дефектное место отмечается номерами цилиндра и головки. Вам необходимо вычислить, какие блоки попадают в дефектную область и занести их номера в таблицу дефектных блоков. Обратная задача также важна. Если программа fsck начинает сооб- щать, что где-то появился дефектный блок, то каким образом мы можем узнать номера цилиндра, головки, сектора и т.д. для данного дефектного блока? Такое обратное вычисление сделать очень тяжело, если не невоз- можно. Во-первых, номер блока представляет собой произведение четырех чисел. Трудно узнать, какие именно эти числа. Кроме того, файловые системы могут использовать информацию вида база/смещение, поэтому блок номер 1 в файловой системе в действительности является блоком номер 1382 на диске. Определить, какого вида информация была использована в данном случае, тоже тяжело. Конкретные данные в следующем примере относятся к вполне опреде- ленной машине, но на других машинах используются подобные зависимости. Эти данные относятся к машине с жестким диском объемом 20 Мбайт с системами XENIX/DOS. Характеристики устройства: 1 диск = 615 цилиндров, или 615 цилиндров/диск 1 цилиндр = 4 головки (дорожки), или 4 головки/цилиндр Промышленный стандарт: 1 дорожка = 17 секторов, или 17 секторов/дорожку 1 сектор = 512 байт, или 512 байт/сектор 1 Кбайт = 1024 байта = 2^10 1 Мбайт = 1024 килобайта = 2^20 = 1 048 576 байт Характеристики устройства различны для разных устройств, но про- мышленный стандарт для числа секторов на дорожку и байтов на сектор остается одинаковым. В табл. 7-4 показаны примеры характеристик раз- личных устройств. Таблица 7-4 Размеры жестких дисков и их конфигурация
Число цилиндров Число головок Мегабайты
981 3 25 697 5 30 981 5 42 925 7 55 1024 8 71
Вы видите, что число цилиндров и число дорожек различны для уст- ройств с разным объемом. Определить максимальный объем дисковой памяти можно перемножением всех чисел. В следующем примере вычисляется общий размер в байтах для предыдущих данных. 615 цил 4 дор 17 сек 512 байт ------- * ----- * ------ * -------- = 21 411 840 байт/диск 1 диск 1 цил 1 дор 1 сек 21411840 байт 1 мегабайт ------------- * ------------ = 20.4 мегабайта/диск 1 диск 1048576 байт Отметим, что если вы верно указываете единицы измерения, то они попарно сокращаются, за исключением одной снизу и одной сверху, и в результате получаются нужные единицы измерения в ответе. Таким обра- зом, в первой строке вычислений цилиндры, дорожки и секторы сокраща- ются, и в качестве единиц измерения остаются байты/диск. Поскольку мы имеем дело с таким большим количеством различных единиц измерения, вы- числения такого рода (иногда называемые "размерный анализ") убеждают нас, что мы понимаем, о чем идет речь. Объем доступной дисковой памяти уменьшается после форматирования, локализации дефектных блоков и размещения на диске файловой системы. Однако наш пример показывает, что все размеры согласуются между собой. Важным моментом, на который необходимо обратить внимание, явля- ется использование разных терминов. Иногда применяется число головок на цилиндр, а иногда число дорожек на цилиндр. При использовании каж- дого из этих терминов соответствующим образом изменяются и другие тер- мины. Существует такая взаимосвязь: цилиндр, дорожка, сектор = физический сектор цилиндр, головка, байт = блок Эти две записи выражают в точности одно и то же. Когда вы исполь- зуете запись вида цилиндр/дорожка/сектор, то в результате получаете физический сектор. Используя запись вида цилиндр/головка/байт, вы по- лучаете в результате номер блока. Следует помнить, что ГОЛОВКА - это то же самое, что и ДОРОЖКА. Если вы это запомните, все остальное вста- нет на свое место. Ниже приводятся некоторые общие вычисления, которые часто всплы- вают, когда вы работаете с диском на низком уровне. Эти примеры в большей степени относятся к самому устройству, чем к системе UNIX. Од- нако после того, как вы поймете эту информацию о диске, вам будет лег- че понять, как работает UNIX на этом уровне. 1. Сколько дорожек имеет диск? 615 цил 4 дор Решение: ------- * ----- = 2460 дор/диск 1 диск 1 цил 2. Сколько байт в дорожке? 17 сек 512 байт Решение: ------ * -------- = 8704 байт/дор 1 дор 1 сек 3. Сколько дорожек в одном мегабайте? 2460 дор 1 диск Решение: -------- * ------ = 123 дор/Мб 1 диск 20 Мб 4. Сколько цилиндров в одном мегабайте? 1 цил 2460 дор Решение: ----- * -------- = 30 цил/Мб, 4 дор 20 Мб 615 цил 1 диск 123 дор или ------- * -------- * ------- = 30 цил/Мб 1 диск 2460 дор 1 Мб 5. Дан цилиндр 47, дорожка 2, сектор 4. Какой физический номер секто- ра? Решение: Сначала мы обращаем внимание на то, что вопрос касается секторов. В качестве единиц измерения даны цилиндр, дорожка и сектор. Как пере- вести их в другие единицы? Мы знаем, что головки - это то же самое, что и дорожки, поэтому в вычислениях нужно использовать 4 головки вместо 4 дорожек: 4 дор 17 сек 17 сек 47 цил * ----- * ------ + 2 дор * ------ + 4 сек = 1 цил 1 дор 1 дор = 3196 сек + 34 сек + 4 сек = = сектор 3234

    РАЗМЕРЫ ФАЙЛОВ

В основном при работе в системе UNIX мы считаем, что ее ресурсы безграничны. Например, мы не заботимся о том, что созданный файл полу- чится "слишком большим", а это не так уж редко в персональных компь- ютерах на гибких дисках. Если же мы занимаемся сопровождением и адми- нистрированием системы UNIX, то мы должны быть готовы иметь дело с си- туациями, когда превышаются различные предельные значения системы. Всегда лучше исследовать эти вопросы заранее в некритичных ситуациях, поэтому давайте рассмотрим пределы размеров файлов и их смысл. Некоторые параметры "зашиты" в ядро системы при ее генерации. Од- ним из таких значений является максимальный размер файла. Он определя- ет наибольшее число блоков, которые может занимать файл. Этот параметр тесно связан с принятым в UNIX методом использования индексных деск- рипторов файла (inodes). Это наборы указателей, среди которых первые десять указывают на блоки данных, следующий указывает на другую табли- цу, следующий - на таблицу, указывающую на таблицу и т.д. Имеется еще одно ограничение размера файла, которое определено для каждого пользователя во время работы в системе - число ulimit (user limit - пользовательский предел). Это значение устанавливается в момент вашей регистрации в системе и представляет собой число блоков по 512 байт, которые вы можете записать в любой заданный файл. В shell'е имеется команда ulimit, которая при ее вызове без аргументов выводит это число. Эта же команда позволяет вам уменьшить ваше значе- ние ulimit. Только суперпользователь (root) может УВЕЛИЧИТЬ значения ulimit. Побочным эффектом уменьшения значения ulimit является то, что вы не можете снова увеличить его до регистрационного значения. Значение ulimit остается таким же на все время работы вашего shell, поэтому для восстановления регистрационного значения вам необходимо выйти из системы, а затем снова зарегистрироваться. Еще одним интересным моментом является то, что если вы установите ваше значение ulimit равным 0, вы не сможете создать никакие файлы! Максимально допустимым размером файла в данном случае является нуле- вой, поэтому никакой файл не может быть создан. Это представляется достаточно резонным, однако существуют такие ситуации, когда файл ну- левого размера МОЖЕТ существовать. Опять же, для восстановления вашего обычного значения ulimit необходимо выйти из системы, а затем снова зарегистрироваться. Как отмечалось ранее, увеличить значение ulimit может только су- перпользователь. Эта процедура довольно проста. Сначала нужно увели- чить значение ulimit командой ulimit, а затем запустить shell. Этот новый shell имеет новое значение ulimit. Если мы хотим, чтобы система загружалась с shell, имеющим большее значение ulimit, мы можем устано- вить программу в inittab (таблице инициализации системы), чтобы эта операция выполнялась автоматически. Ниже приводится пример программы, которая изменяет значение ulimit и запускает shell с этим новым значением. Напомним, что эта программа может быть запущена только суперпользователем. 1 #include 2 #include 4 main() 5 { 6 long v1, v2, v3, newlimit = 5120; 8 v1 = (long)ulimit(UL_GFILLIM, 0L); 9 v2 = (long)ulimit(UL_SFILLIM,newlimit); 10 v3 = (long)ulimit(UL_GFILLIM, 0L); 12 printf("v1: %ld v2: %ld ulim: %ld\n",v1,v2,v3); 13 setuid(getuid()); 14 execl("/bin/sh","ulimit sh", 0); 15 } Значение ulimit является возвращаемым значением системного вызова ulimit. Первый вызов ulimit в строке 8 получает исходное значение по умолчанию. Это значение сохраняется в переменной v1. Вызов в строке 9 устанавливает новое значение ulimit равным значению переменной newlimit. Если этот вызов оканчивается неудачей, переменной v2 присва- ивается возвращаемое значение -1, и мы видим это по распечатке, кото- рую выдает строка 12. Если вызов был успешным, возвращаемым значением является новое значение ulimit, и это мы тоже видим. Затем вызов в строке 10 получает это значение ulimit. Это или новое значение, или старое, в зависимости от того, была ли успешной попытка изменить ulimit. В строке 13 значение идентификатора текущего процесса устанавли- вается равным значению идентификатора пользователя, запустившего дан- ный процесс. Это сработает только в том случае, если пользователь, за- пустивший данный shell, имеет более низкий идентификатор, чем сам про- цесс. Цель заключается в том, чтобы предоставить возможность обычным пользователям запускать данный процесс, давая им временно права супер- пользователя. (Не оставляйте исходный текст этой программы в системе, поскольку кто-то может превратить ее в "лазейку" и перекомпилировать ее - в главе 9 мы увидим такого рода дыры в системе защиты.) Строка 14 запускает shell. Аргументом этого shell является строка "ulimit sh". Эта строка будет выведена на экран, если мы выполним ко- манду "ps -ef". Данный shell имеет новое значение ulimit. Возможность изменить значение ulimit позволяет нам определить на- ибольший возможный размер файла. Создание одного или нескольких таких файлов максимального размера полезно в целях тестирования. Например, полезно выяснить, сколько данных может содержать гибкий диск без пере- полнения или что произойдет, когда система выйдет за пределы свободных блоков. Мы хотим понять, как ведет себя система в таких ситуациях.
ИМЯ: umntsys
umntsys

    НАЗНАЧЕНИЕ

Размонтирование всех файловых систем, смонтированных в данный мо- мент.

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

umntsys

    ПРИМЕР ВЫЗОВА

umntsys Размонтирует все смонтированные файловые системы

    ТЕКСТ ПРОГРАММЫ

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

    ПОЯСНЕНИЯ

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

    НАЗНАЧЕНИЕ

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

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

lrgf

    ПРИМЕР ВЫЗОВА

lrgf Определение границы размера файла

    ТЕКСТ ПРОГРАММЫ

1 char id[] = "@(#) lrgf v1.0 Create the largest file Author: Russ Sage Создать файл максимального размера 3 #include 4 #include 5 #include 6 #include 8 #define FSIZ 512 9 #define BSIZ 1024 11 long ulimit(); 12 char buf[BSIZ]; 14 main() 15 { 16 register int n, fd, bcnt; 17 char file[FSIZ]; 19 for (bcnt=0; bcntОПИСАНИЕ ЗАЧЕМ НАМ НУЖНА ПРОГРАММА lrgf? Как обсуждалось ранее, нам необходимо знать, что происходит, ког- да UNIX достигает каких-то пределов. Не только пределов размера файла, а любых пределов. Примерами предельных значений являются количество процессов, которые вы можете запустить, общее количество процессов в системе, количество файлов, которые вам разрешено открыть, количество свободных блоков, количество индексных дескрипторов, глубина вложен- ности каталогов, при которой еще возможна работа, и т.д. Нам нужна программа, создающая файл максимального размера. Затем мы можем использовать этот файл для проверки различных пределов, имею- щих отношение к файлам. ЧТО ДЕЛАЕТ lrgf? Lrgf - это программа, которая создает файл максимально возможного размера. Выполняется это путем записи в файл до тех пор, пока команда записи не окончится неудачей. Это означает, что в данный файл больше нельзя записать данные, и он достиг границы. При вызове программа lrgf выводит сообщение, содержащее общее ко- личество байтов, которое вы можете записать в файл. Это значение вы- числяется по значению ulimit, поэтому оно разное для разных пользова- телей и зависит от shell, с которым пользователь сейчас работает. Затем у вас запрашивается имя файла. Вы можете указывать любое имя, для которого вы имеете права записи. Одним из способов проверки жесткого диска является запись одного из таких больших файлов в каждый раздел диска и затем проверка отдельных файловых систем. После запуска программы вы увидите строку с сообщением, которое постоянно обновля- ется. Оно содержит общее число записанных блоков и количество байтов, которое записывалось при каждой попытке записи. Программа lrgf записы- вает в файл каждый раз по 1024 байта. В зависимости от вашего значения ulimit количество байтов, дописываемых в конец файла, может не быть равным в точности 1 Кб. Выходное сообщение постоянно печатается в од- ной и той же строке, заменяя на экране старое значение. Это достига- ется путем вывода только символа возврата каретки, а не перевода стро- ки. Когда программа не может больше записывать данные в файл, итого- вое количество блоков выводится на экран. Это общее число блоков, за- писанных в файл.

    ПРИМЕРЫ

1. $ lrgf /dev/rfd0 Ввод имени устройства в ответ на запрос имени файла, в который будет производиться запись. При этом программа lrgf выполняет последо- вательную запись на гибкий диск неструктурированных данных. Тем самым проверяется, распознает ли драйвер устройства переполнение гибкого диска. Это важно знать при работе с командой cpio, которая предполага- ет, что драйвер устройства сообщит об остановке и запросе следующей дискеты. 2. $ lrgf /usr/tmp/lrg Создание файла в файловой системе /usr. Большинство систем XENIX используют каталог /usr как отдельную файловую систему, отличную от корневой. Созданием файла в каталоге /usr /tmp мы можем проверить по- ложение дел в этой часто используемой файловой системе. 3. $ lrgf /tmp/lrg В данном случае создаваемый файл займет место в корневой файловой системе (если вы не имеете каталога /tmp в вашей собственной файловой системе). Потребуется не слишком много таких файлов для заполнения всех свободных блоков в корневой файловой системе. 4. $ lrgf /mnt/lrg Создание файла на гибком диске в предположении, что на гибком диске имеется файловая система и она смонтирована в каталог /mnt. 5. $ F=0 $ while : > do > echo -r "--> Making file $F <--" > ./lrgf <<-! > $F > ! > echo > F=`expr $F + 1` > done Данный цикл запускает программу lrgf бесконечное число раз. Счет- чиком является переменная F. Она должна быть предварительно установле- на в нуль, чтобы shell рассматривал ее как число, а не как символьную строку. Сначала выводится сообщение, содержащее имя создаваемого фай- ла. Первым именем файла является 0. Программа lrgf запускается, используя в качестве входных данных "данный документ" (т.е. сам ко- мандный файл). В качестве ответа на вопрос об имени файла используется значение $F. Значение переменной F увеличивается, и программа lrgf вы- зывается снова. Именами файлов являются 0, 1, 2 и т.д. Это продолжа- ется до тех пор, пока не останется больше свободного места. Вряд ли вы будете пользоваться этим часто, но для тестирования это прекрасное средство заполнить все свободное пространство. Если вы хотите увидеть, что делает ваша система, когда исчерпаны свободные блоки, примените данный командный файл.

    ПОЯСНЕНИЯ

Строки 3-6 включают все необходимые файлы заголовков. Эти файлы содержат определения и метки, необходимые данной программе. Строки 8 и 9 определяют размеры буфера для имен файлов и буфера для записи на диск. Значение BSIZ можно поднастроить, если программа работает слишком медленно. У вас может возникнуть желание увеличить BSIZ до 4096, чтобы производилось не так много операций записи. Строка 11 определяет возвращаемое значение системного вызова ulimit как длинное целое. Строка 12 резервирует буфер, который должен быть записан. Этот буфер находится вне основной части программы из-за ограничений на размер внутри функций. В основном блоке программы наи- большая область автоматической памяти, которую вы можете иметь, равна размеру вашего стека. Вы можете сделать по-другому, объявив данный бу- фер как статическую переменную в функции main. Мы решили вынести его за пределы функции main и не объявлять как статическую переменную. Строка 16 объявляет некоторые рабочие переменные. Заметим, что они помещаются в регистры. Это сделано для ускорения работы программы и получения более компактного объектного кода. Строка 17 резервирует буфер, в который вы вводите имя файла. Строки 19 и 20 заполняют записываемый буфер символами "x", поэто- му после создания файла мы можем их там увидеть. Строка 22 выводит значение ulimit для вашего процесса. Обратите внимание, что вызов ulimit возвращает количество блоков, поэтому мы должны умножить это число на 512. В результате мы получим общее коли- чество байтов, которое может содержать файл. Строки 24-26 запрашивают имя файла, читают его и подготавливают экран для следующего сообщения. Строки 28-32 открывают файл с указан- ным именем для получения дескриптора файла. Файл открывается для за- писи и чтения, создается при необходимости и обрезается, если он уже существует. Если операция открытия файла заканчивается неудачей, выво- дится сообщение об ошибке, и программа завершается. Строки 34-42 выполняют запись. Цикл for бесконечен, поскольку в середине оператора нет проверки значения счетчика. Переменная bcnt постоянно увеличивается, пока выполняется запись. Строка 36 выполняет запись в файл. Если запись неудачна, выво- дится сообщение об ошибке и по оператору break осуществляется выход из цикла. Строка 41 выводит количество выполненных операций записи и ко- личество записанных байтов. Обратите внимание, что данный оператор print содержит возврат каретки (\r), а не перевод строки. Это позволя- ет курсору оставаться в одной итемах. экране поверх старых значений. Экран не скроллируется, что удобно для наблюдения. Выполнение цикла продолжается до тех пор, пока системный вызов write не закончится неудачей и оператор break не прекратит цикл. Когда это происходит, выполнение продолжается со строки 43, которая печатает "end of program". Выполнение команды "ls -l" для записанного файла показывает, сколько байтов имеет файл максимального размера. Это количество должно совпадать с числом, которое сообщила вам программа lrgf. В данной главе представлена лишь небольшая часть возможных ислле- дований внутренней работы файловых систем и устройств в UNIX. Некото- рые из представленных программ могут быть неприменимы в вашей версии системы UNIX или в вашей конфигурации аппаратных средств или могут выглядеть в вашей системе иначе. Однако общие принципы сохраняются, и вы можете использовать рассмотренные средства в качестве основы для ваших собственных исследований.

    * ГЛАВА 8. Коммуникации в системе UNIX *

ВВЕДЕНИЕ ФИЗИЧЕСКОЕ ПОДКЛЮЧЕНИЕ ПОДКЛЮЧЕНИЕ БЕЗ КОММУНИКАЦИОННЫХ УСТРОЙСТВ ДОСТУП МИКРО-ЭВМ ИЛИ ТЕРМИНАЛА К СИСТЕМЕ UNIX ПРЯМОЕ ПОДКЛЮЧЕНИЕ ДИСТАНЦИОННОЕ ПОДКЛЮЧЕНИЕ ДОСТУП ИЗ СИСТЕМЫ UNIX К МИКРО-ЭВМ ОБНАРУЖЕНИЕ МОДЕМОВ В СИСТЕМЕ ИСПОЛЬЗОВАНИЕ И КОНФИГУРИРОВАНИЕ ЛИНИИ ИЗМЕНЕНИЕ СКОРОСТЕЙ ОБМЕНА ЗАХВАТ ДАННЫХ ИНСТРУМЕНТАЛЬНЫЕ СРЕДСТВА ДЛЯ КОММУНИКАЦИОННЫХ ЛИНИЙ cuchk - Cu check - проверка свободной линии для cu talk - Обращение к последовательному порту ДОСТУП ИЗ UNIX В UNIX СВЯЗЫВАНИЕ UNIX-МАШИН ОБЛАСТИ ДЛЯ ПЕРЕДАВАЕМЫХ ФАЙЛОВ СЕТЕВАЯ БЕЗОПАСНОСТЬ ОГРАНИЧИТЕЛЬНЫЕ ФАЙЛЫ ОТЛАДКА КОММУНИКАЦИЙ uust Состояние uucp и служебные действия uutrans Передача файловых деревьев из одной системы UNIX в другую систему UNIX КОНФИГУРАЦИОННЫЕ РЕШЕНИЯ ПОДКЛЮЧЕНИЕ UNIX К МОДЕМУ И ГЛАВНОЙ МАШИНЕ ПОДКЛЮЧЕНИЕ UNIX И ТЕРМИНАЛА К МОДЕМУ ПОДКЛЮЧЕНИЕ UNIX К ТЕРМИНАЛУ, МОДЕМУ И ГЛАВНОЙ МАШИНЕ СИСТЕМА UNIX, МИКРО-ЭВМ И МОДЕМ АЛЬТЕРНАТИВНОЕ РЕШЕНИ ТРИ СИСТЕМЫ UNIX

    ВВЕДЕНИЕ

В данной главе мы рассматриваем средства коммуникации в системе UNIX. В то время как в мире MS-DOS коммуникации ограничены обычно "черным ящиком" (программными пакетами и довольно стандартными модемами), коммуникации в системе UNIX более сложны. UNIX предлагает несколько уровней коммуникаций, включая передачу файлов, удаленную регистрацию в системе, дистанционную почту и развитые системы обмена сообщениями, которые могут связывать между собой сотни систем UNIX. Однако, в большинстве систем UNIX нет удобно оформленных, управляемых с помощью меню средств коммуникации. Вместо этого имеются сложные системные команды и необходимость поддерживать много файлов. Обычно требуется также владеть многими подробностями конфигурации и использования модемов. Мы предлагаем теоретическое обоснование и практические рекомендации, а также инструментальные средства, которые помогут вам освоить данный аспект системы UNIX и обеспечить работоспособность коммуникаций в вашей системе. Мы рассмотрим не только прямую связь между машинами, но и связь с удаленными терминалами и модемами. Сначала мы займемся физическими соединениями, что является первым этапом установки линии связи. Мы рассмотрим модель интерфейса RS232-C и выясним, как подключить прямую межмашинную связь. Затем мы обсудим обращение к системе UNIX с микро-ЭВМ. Поговорим о том, какие существуют виды протоколов и какие из них лучше использовать. Далее мы рассмотрим, как пользоваться модемом, чтобы вызвать из UNIX другие системы, например доски объявлений и системы, отличные от UNIX. Вы можете изучить, как найти все модемные соединения в системе, подключиться к последовательному порту и управлять модемом. При обращении к другой системе вы можете перехватить все данные, поступающие на ваш терминал, и сохранить их в файле для последующего использования. Командные файлы (cuchk и talk), представленные в данном разделе, следят за доступностью линии связи и устанавливают связь с модемом. В завершение мы рассмотрим связь между системами UNIX с помощью утилиты uucp. Мы увидим, как и куда передавать файлы между системами, как файлы защиты данных управляют средой uucp и изучим способы отладки механизма передачи файлов с помощью uucp. Здесь представлены командные файлы uust для выполнения рутинной работы по обслуживанию uucp и uutrans для копирования древовидной структуры каталогов из одной системы в другую.

    ФИЗИЧЕСКОЕ ПОДКЛЮЧЕНИЕ

Большинство машин, на которых работает UNIX, имеют один или несколько последовательных портов. Эти порты - глаза и уши машин, позволяющие системе связываться с внешним миром. Их можно использовать для подключения любого устройства с интерфейсом RS-232-C и для связи или управления. В данном разделе мы рассмотрим, как подключить интерфейс RS -232-C для обеспечения связи типа UNIX-UNIX, терминал-UNIX и модем-UNIX. Начнем с рассмотрения базовой модели RS-232-C, показанной на рис. 8-1. Эта модель иллюстрирует, как могут общаться друг с другом две машины и/или терминалы либо через модемы по телефонным линиям, либо по прямой (проводной) связи. Хотя последующее обсуждение мы ведем преимущественно в терминах телефонных соединений, те же базовые принципы относятся и к прямой связи, за исключением того, что коммуникационные устройства (DCE, data communication equipment) в этом случае не нужны. Рис. 8-1. Стандартная модель интерфейса RS-232-C
_||_ +-------+ 2 ***** _||_ ***** 2 +-------+ | |------>* *----\ || /----* *<------| | | DTE-1 | * DCE * \ || / * DCE * | DTE-2 | | |<------* * / || \ * *------>| | +-------+ 3 * *<---/ || \--->* * 3 +-------+ ***** || *****
На каждом конце находятся терминальные устройства, называемые DTE (data terminating equipment). В роли DTE может выступать терминал, например, VT-100, или центральный процессор микро-, мини- или большой ЭВМ. Каждое терминальное устройство DTE должно использовать коммуникационное устройство DCE, называемое обычно модемом, для модуляции и демодуляции сигналов, проходящих по телефонным линиям. Каждое DTE использует вывод номер 2 для передачи данных и вывод номер 3 для получения данных. Поскольку то, что передано с вывода 2 на каждой машине, принимается на выводе 3 другой машины, возникает перекрещивание телефонных линий между устройствами DCE. Подсоединение и обработка сигнала между DTE и DCE полностью соответствуют стандарту RS-232-C. Аппаратный протокол позволяет DTE использовать DCE для посылки и приема данных от другого DTE. Кабель, связывающий физически DTE и DCE, называется "прямолинейным" кабелем. Он позволяет устройству DTE посылать команды (или сигналы с выводов) на DCE, а устройству DCE отправлять команды обратно на DTE. Подключение DCE одной машины к DCE другой машины производится через обычные телефонные линии. Устройства DCE необходимы по той причине, что устройства DTE являются цифровыми, а телефонные линии - аналоговыми. Единственный способ передать цифровую информацию по аналоговым линиям - закодировать цифровую информацию в аналоговый сигнал, послать этот сигнал по телефонным линиям, а затем декодировать аналоговый сигнал обратно в цифровую информацию.

    ПОДКЛЮЧЕНИЕ БЕЗ КОММУНИКАЦИОННЫХ УСТРОЙСТВ

Если ваши машины расположены довольно близко (в пределах 50 футов / 15 метров. Один фут составляет около 30.5 см. - Примеч. перев./), вам не нужен модем, вы можете использовать кабель "нулевого модема" вместо DCE. Кабель нулевого модема имитирует такой же протокол, что и DCE, но не требует наличия модема для коммуникаций. Основная задача подключения нулевого модема - обеспечить перекрещивание между передающими и принимающими сигналами. На рис. 8- 2 показана общая схема подключения без устройств DCE. Рис. 8-2. Конфигурация с нулевым модемом
+-------+ 2 2 +-------+ | |------>... ...<------| | | DTE | 3 \./ 3 | DTE | | |<------.../ \...------>| | +-------+ +-------+
Для того чтобы выполнить подключение, имитирующее DCE, требуются некоторые манипуляции с сигналами. Эти манипуляции также стандартизованы в кабеле нулевого модема. По схеме этого кабеля, показанной на рис. 8-3, рассмотрим, как он имитирует сигналы DCE. Рис. 8-3. Кабель нулевого модема RS-232-C
DTE-1 DTE-2 || || ЗАЩИТНАЯ ЗЕМЛЯ 1 ||-------------------------|| 1 (PROTECTIVE GROUND) || || || || СИГНАЛЬНАЯ ЗЕМЛЯ 7 ||-------------------------|| 7 (SIGNAL GROUND) || || || || ПЕРЕДАЧА ДАННЫХ 2 ||----------\ /----------|| 2 (TRANSMIT DATA) || . || ПРИЕМ ДАННЫХ 3 ||<---------/ \--------->|| 3 (RECEIVE DATA) || || || || ЗАПРОС ПЕРЕДАЧИ 4 ||----- -----|| 4 (REQUEST TO SEND) || | | || || | | || ГАШЕНИЕ ПЕРЕДАЧИ 5 ||<----\ /---->|| 5 (CLEAR TO SEND) || \---\ /---/ || || . || ИДЕТ ПЕРЕДАЧА ДАННЫХ 8 ||<---------/ \--------->|| 8 (DATA CARRIER DETECT) || || || || ГОТОВНОСТЬ НАБОРА ДАННЫХ 6 ||<---------\ /--------->|| 6 (DATA SET READY) || . || ГОТОВНОСТЬ ТЕРМИНАЛА 20 ||----------/ \----------|| 20 (DATA TERMINAL READY) || || || ||
Линии 1 и 7 используются для шасси и сигнальной земли соответственно. Линии 2 и 3 пересекаются таким образом, чтобы когда одна сторона говорит, другая слушала. Обе стороны могут говорить одновременно (это называется полнодуплексным режимом), если мы используем различные наборы проводов. Для имитации управляющих сигналов линии 4, 5 и 8 подсоединяются так, как показано на рис. 8-3. Каждый раз, когда устройство DTE-1 активизирует линию "Request to Send" ("Запрос передачи"), т.е. передает по ней сигнал, оно получает назад сигнал "Clear to Send" ("Гашение передачи"), указывающий, что другая сторона готова принять данные. Затем, посылая сигнал по линии "Dаta Carrier Detect" ("Идет передача данных"), устройство DTE-1 сообщает другой стороне, что поступают данные. Такое методичное "аппаратное рукопожатие" гарантирует, что никакие данные не будут отправлены, пока другая сторона не будет готова их принять. Линии 6 и 20 подсоединяются так, чтобы обеспечить последние управляющие сигналы нулевого модема. Пока DTE активно ("Data Terminal Ready" - "Готовность терминала", линия 20), другая сторона считает, что имеет дело с активным модемом ("Data Set Ready" - "Готовность набора данных", линия 6). При таком способе соединения линий 6 и 20 всякий раз, когда вы выдергиваете ваш кабель из машины или переключаете его на другой канал соединительной коробки, другая сторона теряет ваш сигнал активности и отключается (или генерирует сигнал HUP - hangs up, повесить трубку телефона). Чтобы сделать такой кабель, который не вызывает отключения при вынимании штепселя (т.е. NOHUP), присоедините выход "Data Terminal Ready" ко входу "Data Set Ready" на том же устройстве DTE. Это заставляет систему сообщать самой себе, что модем всегда готов. Заметим, что рассмотренная схема подключения нулевого модема является рекомендуемой, но существуют и другие способы, поэтому не думайте, что все нулевые модемы одинаковы. В каждом конкретном случае для нулевых модемов учитывается определенное окружение или функция, например наличие безобрывного (nohup) варианта подключения. Теперь, когда мы знаем два различных способа соединения машин, мы можем рассмотреть способы коммуникаций и типы подключения, которые могут нам пригодиться. ДОСТУП МИКРО-ЭВМ ИЛИ ТЕРМИНАЛА К СИСТЕМЕ UNIX В этом разделе мы рассмотрим различные способы общения микро-ЭВМ и автономных терминалов с системой UNIX. Мы предполагаем, что одно устройство DTE работает не под управлением UNIX и обращается к другому устройству, управляемому системой UNIX.

    ПРЯМОЕ ПОДКЛЮЧЕНИЕ

В простейшем случае имеется терминал или микро-ЭВМ, подсоединенные непосредственно к системе UNIX. Это очень часто встречается в системах разработки, когда UNIX используется в качестве кросс-компилятора, а результирующий код загружается в микро-ЭВМ. Другая ситуация - когда терминалы находятся на рабочих столах сотрудников и применяются для выполнения бумажной работы, отправки почты, печати документов и т.д. Типичные конфигурации с прямым подсоединением показаны на рис. 8-4. Обычный сценарий подключения терминала выглядит примерно таким образом. Пользователь с помощью терминала, например DEC VT-100, регистрируется в системе UNIX по прямой связи. Терминальное устройство DTE должно быть установлено в соответствии с правильной внутренней конфигурацией, включая скорость в бодах, стартовые и стоповые биты, число битов данных и четность. Это обычные установки терминала, которые можно задать либо тумблерными переключателями, либо при помощи терминальной микропрограммы "SETUP". Микро-ЭВМ, в отличие от терминала, должна запустить некоторое коммуникационное программное обеспечение для корректного управления аппаратурой. Эти программы обычно снабжены меню или каким-то другим способом указания параметров - таких же, как у терминалов. Действительно, имеются программные пакеты, обеспечивающие полную или почти полную эмуляцию популярных терминалов, таких как VT-100. Коммуникацинное программное обеспечение, используемое вами, устанавливается не специально для UNIX, а для обработки сигналов RS-232-C, которые обычно не зависят от того, прямое ли подсоединение или через модем. Рис. 8-4. Прямое подключение терминалов и микро-ЭВМ к UNIX
DTE-1 DTE-2 ДЕЙСТВИЕ ДЕЙСТВИЕ +----------+ +--------+ Микропрограмма | | Нулевой модем| | getty 9600 tty00 SETUP | терминал |--------------| tty00 | login имя_польз | | | | sh +----------+ | | | UNIX | +----------+ | | Коммуникационное | | Нулевой модем| | программное | микро-ЭВМ|--------------| tty01 | обеспечение | | | | +----------+ +--------+
В качестве кабеля, соединяющего терминал (или микро-ЭВМ) с системой UNIX, нужно использовать нулевой модем. Когда коммуникационное устройство DCE отсутствует, применяется кабель нулевого модема. В системе UNIX на терминальном устройстве DTE должна быть утилита getty (что первоначально означало "get teletype" - доступ к телетайпу), которая работает с определенным портом и обнаруживает момент, когда кто-то пытается зарегистрироваться в системе. Программа getty (описанная в init(M) и getty(M)) выводит подсказку "login:" и читает символы, поступающие по линии. При прямом подключении процесс getty может работать со скоростью 9600 бод, поэтому пользователи могут получить выигрыш от применения этой скорости. Это значительно быстрее, чем 1200 или иногда 2400 бод - скоростей, которые используются чаще всего при работе с обычными телефонными линиями. В зависимости от того, как установлен файл gettydefs, вы можете обычно изменить скорость работы getty, нажимая клавишу break. Клавиша break генерирует не символ, а сигнал в линию, который длится определенный интервал времени. Драйверы устройств в системе UNIX распознают этот сигнал и действуют соответствующим образом. Рассмотрение самого метода, с помощью которого getty работает на различных скоростях, выходит за пределы данной книги. Важно знать, что если getty работает на скорости 9600 бод, а вы используете терминал, установленный на 1200 бод, то вам нужно прокрутить переключение скоростей getty с 9600 бод через все промежуточные скорости до достижения значения 1200 бод. Держите клавишу break нажатой до тех пор, пока getty не установится на нужную вам скорость. Следующий пример взят из файла gettydefs в OC XENIX System V и показывает, как связаны между собой скорости обмена.
| 5# B9600 PARENB CS7 OPOST ONLCR # B9600 SANE IXANY #Login: #1 | 4# B4800 PARENB CS7 OPOST ONLCR # B4800 SANE IXANY #Login: #5 | 3# B2400 PARENB CS7 OPOST ONLCR # B2400 SANE IXANY #Login: #4 | 2# B1200 CS8 OPOST ONLCR # B1200 SANE IXANY #Login: #3 | 1# B300 CS7 OPOST ONLCR # B300 SANE IXANY #Login: #2 | Здесь указание #5 в файле inittab (или в файле ttys для пользователей XENIX и System III) порождает процесс getty, работающий на скорости 9600 бод. Если вы нажимаете клавишу break, следующим по порядку является номер 1 (как указано в конце строки номер 5). Если вы нажимаете break еще раз, то следующей является строка номер 2, и т.д. Если вы нажмете break достаточное количество раз, скорость getty вернется по циклу к значению 9600 бод. Вся последовательность действий, связанных с регистрацией в системе, выглядит примерно так. Сначала init порождает процесс getty (инициируемый из файла /etc/inittab) на определенном номере терминала и с определенной скоростью. При этом устанавливаются характеристики линии и выдается регистрационная подсказка. Когда пользователь вводит регистрационное имя, getty проверяет его на правильность, а затем выполняет программу login. Login запрашивает пароль, зашифровывает его и сверяет с зашифрованным паролем в файле /etc/passwd. Если пароли совпадают, login запускает процесс shell, который печатает shell-подсказку и читает ваши команды, поступающие с терминала. Какой именно shell будет запущен - определяется записью в файле паролей, соответствующей данному регистрационному имени. Моему регистрационному имени соответствует такая запись: russ:j9egLecqEpXLg:201:51:Russ Sage:/usr/russ:/bin/shV В этом случае запускается System V shell, один из новых shell-интерпретаторов фирмы AT&T.

    ДИСТАНЦИОННОЕ ПОДКЛЮЧЕНИЕ

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

    ОБНАРУЖЕНИЕ МОДЕМОВ В СИСТЕМЕ

Если вы работаете на большой машине, вам нужно выяснить, какие порты в вашей системе подсоединены к модему и внешней линии. Если вы администратор, то вы должны знать, как сконфигурировать эти линии. Начинать нужно с файла /usr/lib/uucp/L-devices. Это основной файл, определяющий, какие порты используются и каким образом они используются. Вот пример распечатки с информацией о портах:
| | ACU cul0 cua0 1200 | DIR tty00 0 300 | DIR tty00 0 1200 | DIR tty00 0 2400 | DIR tty00 0 4800 | DIR tty00 0 9600 | В файле L-devices каждый порт определяется либо как прямое подключение (DIR - direct connection), либо как специальное устройство, называемое устройством автоматического вызова (ACU - automatic call unit). Об устройствах вызова мы поговорим позже. Сейчас нам нужно рассмотреть только записи типа DIR. Программа cu использует записи типа прямого подключения, а uucp - типа ACU. Теперь очень просто идентифицировать каждый последовательный порт: как к нему обращаться, на какой скорости он работает и можем ли мы осуществить вызов через этот порт. Данные файла L-devices показывают, например, что последовательный порт tty00 используется как линия прямого вызова. Вам разрешается использовать для вызова через этот порт скорости от 300 до 9600 бод. Это полный диапазон используемых обычно скоростей обмена, причем более высокие скорости применяются для прямого подключения к другим машинам, а не к модему. А как насчет модемных линий, которые ВХОДЯТ в машину? Для того чтобы их найти, нужно посмотреть каталог /etc. Там два файла dialin и dialup определяют, на каких линиях tty применяется вторичная регистрационная парольная последовательность для удаленных пользователей. Эти линии tty предназначены только для обращения извне в данную систему и обычно не могут быть использованы для доступа из этой системы во внешний мир.

    ИСПОЛЬЗОВАНИЕ И КОНФИГУРИРОВАНИЕ ЛИНИИ

Теперь, когда мы знаем, по какой линии осуществить вызов, нам необходима команда вызова. В последних версиях System V утилита cu более развита, чем в предыдущих версиях. Нам, как правило, не нужны особо мощные возможности, поэтому наши командные строки довольно просты и смогут работать практически с любым вариантом программы cu. К основным возможностям, которые мы имеем, относятся: выбор номера линии (т.е. устройства tty), скорости функционирования и подключаться ли к устройству напрямую для ручного набора номера либо же набирать номер автоматически. Самый простой способ - чтобы номер набирала программа cu, но если это невозможно, то вы должны подключиться непосредственно к модему. Следующий пример показывает, как непосредственно подключиться к устройству и вручную управлять модемом с целью набора номера. В данном случае используется довольно широко распространенный модем Hayes 1200 Smartmodem. Если у вас модем, который не является "Hayes-совместимым", то вы должны найти эквивалентные управляющие последовательности в вашей документации. $ cu -ltty00 dir # Получить прямой доступ к терминальной # линии со скоростью 1200 бод Connected # Отзыв программы cu ATdt555-1212 # Внимание модему, установка связи путем # набора следующего номера CONNECT # Модем подтверждает факт подключения # Отправка возврата каретки в систему-адресат Welcome to Micro BBS # Получение приветствия от микро-ЭВМ . . выполнение сеанса работы с удаленной системой . . exit # Выйти из микро-системы ИЛИ, +++ # если вы этого не хотите, временно # выйти из модемного подключения # (по команде escape, а не exit) OK # Ответ модема на временный выход ATh0 # Попросить модем занять телефон OK # Ответ модема ~. # Завершить работу с cu Для того чтобы указать программе cu автоматический набор номера, используйте другой синтаксис в командной строке. Мы применяем устройство автоматического вызова ACU для того, чтобы сделать вызов. Фактическая команда набора номера генерируется программой /usr/lib/uucp/dial. Пример вызова выглядит так: $ cu -acua0 555-1212

    ИЗМЕНЕНИЕ СКОРОСТЕЙ ОБМЕНА

Модемы типа Smartmodem могут работать со скоростями 300 и 1200 бод. По умолчанию, программа cu подключается к последовательному порту на скорости 1200 бод. Если вы подключаетесь с использованием умолчаний, проверьте скорость, временно выйдя из программы cu (по команде escape, а не exit), и применив команду stty, которая отображает скорость обмена: ~!stty < /dev/tty00 Эта команда должна сообщить обычные установки stty для последовательного порта /dev/tty00. Скорость обмена должна равняться 1200. Модемы типа Smartmodem автоматически понижают скорость до 300 бод при ответе по телефону. При вызове вы можете понизить скорость модема до 300 бод двумя различными способами. Первый способ - заставить cu открыть линию со скоростью 300 бод, указав 300 в командной строке программы cu. Заметим, что когда вы применяете альтернативную скорость (отличную от 1200 бод), запись о скорости обмена ДОЛЖНА присутствовать в файле L-devices И в командной строке cu, как в следующем примере: $ cu -ltty00 -s300 dir Второй метод хитрее, но зато предоставляет больше гибкости. Мы можем вызвать cu со значением по умолчанию 1200, затем временно (по команде escape) выйти из cu и вызвать программу stty, чтобы указать скорость обмена последовательного порта равной 300 бодам. Это нужно сделать после того, как вы обратились к линии с помощью cu. Эта измененная скорость обмена действительна до тех пор, пока вы не закроете линию (т.е. окончательно выйдете из cu по команде exit). В этот момент порт инициализируется заново. Например: $ cu -ltty00 dir # Захватить линию на 1200 бод Connected # Отзыв программы cu ~!stty 300 < /dev/tty00 # Временно выйти из cu и # установить для линии tty # скорость 300 бод Отметим, что этот метод НЕ требует наличия записи о скорости 300 бод в файле L-devices, поскольку мы работаем с последовательным портом вне области действия программы cu. Попутно заметим, что временный выход (по команде escape) из cu совершенно аналогичен временному выходу из любой команды системы UNIX. Вы можете временно выйти для запуска определенной команды, например: ~!echo "you can run any command here" Вы можете также перейти в shell и работать так, как вы всегда работаете: ~!sh Вы должны, однако, помнить, что вы все еще подключены с помощью программы cu к последовательному порту, а телефон по-прежнему подсоединен к удаленной системе до тех пор, пока вы не дадите вручную команду выхода или отключения телефона, или пока удаленная система не отключит телефон по тайм-ауту.

    ЗАХВАТ ДАННЫХ

Теперь мы умеем найти модем, подключиться к последовательной линии и установить связь с другой системой. Если мы вызываем доску объявлений, нам может понадобиться захватывать файлы с целью последующего использования. Программа cu не предоставляет такой возможности. Как мы уже отмечали, микро-ЭВМ делают это обычно при помощи RAM-буфера, который сохраняется на диске, когда он заполняется. А UNIX не может действовать таким образом. Простейший способ, придуманный мною для захвата данных из другой системы - пропустить по конвейеру все, что выдается на терминал, через команду tee системы UNIX. Весь ввод с вашей клавиатуры не перехватывается, а все, что идет на стандартное устройство вывода (stdout) попадает в выходной файл утилиты tee. Это выглядит так: cu -ltty00 dir | tee файл_перехвата Одно из последствий перехвата информации таким методом состоит в том, что все символы возврата каретки, печатаемые из удаленной системы, попадают в выводной файл команды tee. Вы обнаруживаете их в виде "^M" в конце каждой строки файла. Избавиться от всех этих возвратов каретки легко, попробуйте выполнить такую последовательность команд редактора ed: ed файл_перехвата 1,$s/^M//g w q Смысл этих команд следующий. Для каждой строки во всем файле (1, $) заменить возврат каретки (^M) на ничего (//). Сделать это для произвольного количества символов возврата каретки в отдельной строке ("g" означает глобальную замену в строке). Вы можете превратить control-M в заменяемую строку либо вводом символа обратной косой черты (\) и настоящим нажатием на клавишу возврата каретки, либо в редакторе vi использовать префикс control-V для разрешения ввода управляющих символов. После редактирования запишите файл и выйдите из редактора ed. Конечно, все это вы можете для удобства оформить в виде командного файла интерпретатора shell.

    ИНСТРУМЕНТАЛЬНЫЕ СРЕДСТВА ДЛЯ КОММУНИКАЦИОННЫХ ЛИНИЙ

Две утилиты, которые мы здесь предлагаем, связаны с доступом к последовательному порту. Когда линия освобождается, вы сразу же узнаете об этом и можете управлять линией с помощью нескольких нажатий клавиш.
ИМЯ: cuchk
cuchk Cu check - проверка свободной линии для cu

    НАЗНАЧЕНИЕ

Опрашивает таблицу состояния процессов и ищет процессы cu. Когда они завершаются, на ваш экран выдается сообщение и программа прекращает работу.

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

cuchk - Сообщить, когда cu освободит линию

    ПРИМЕР ВЫЗОВА

cuchk

    ТЕКСТ ПРОГРАММЫ

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

    ПОЯСНЕНИЯ

В строках 4-8 выполняется проверка на ошибки. Поскольку cuchk не имеет никаких опций, в командной строке не должно быть никаких параметров. Если параметры присутствуют, это считается ошибкой. Строки 10-15 организуют вечный цикл while. Обратите внимание, что амперсанд в строке 15 автоматически запускает cuchk в фоновом режиме, ведь нет никакого резона выполнять эту программу в приоритетном режиме. В строке 12 выполняется команда "ps -e", которая выдает информацию о состоянии всех значительных пользовательских процессов. Эти данные пропускаются по конвейеру через fgrep. Утилита fgrep ищет наличие символов "cu". Мы надеемся, что эти символы соответствуют только процессам cu, которые мы ищем. Они могут, однако, относиться к чему-то совершенно неожиданному, например "picuser" или к какому-то аналогично построенному имени. Результат работы команды fgrep направляется на устройство /dev/null, чтобы избавиться от лишней информации. Нас интересует только статус завершения команды fgrep. Он сообщает нам, найдено ли вхождение символов "cu" или нет. В конце строки 12 присутствует символ обратной косой черты, указывающий интерпретатору shell, что следующая физическая строчка является на самом деле частью той же самой программной строки и что нужно добавить ее к строке 12, а не выполнять самостоятельно. Разбиение длинных строк на несколько меньших строк с помощью символов обратной косой черты позволяет нам получать красиво оформленный, более наглядный исходный код. Строка 13 выполняется в том случае, если fgrep отработала успешно (т.е. если она выдает нулевой статус завершения). Это означает, что символы "cu" найдены, а значит процесс cu работает и в настоящее время не доступен. Поэтому процесс cuchk засыпает на 5 секунд. Когда он пробуждается, выполнение продолжается со строки 10, возобновляется вечный цикл и проверка процессов продолжается. Если fgrep не находит символы "cu" (возвращается ненулевой статус завершения), то выполняется строка 14, которая выдает сообщение о том, что программа cu свободна, и работа программы завершается. Таким образом, мы сразу же знаем, что линия cu открыта для использования, поэтому мы можем поскорее занять ее.
ИМЯ: talk
talk Обращение к последовательному порту

    НАЗНАЧЕНИЕ

Выполняет командную строку, подготавливающую последовательный порт для общения с другой системой.

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

talk [-bBAUD] [-l] [-tTTY] [-u] Опции: -b установить новую скорость обмена -l протоколировать все поступающие данные -t использовать другой порт tty -u использовать принятую в UNIX скорость обмена 9600

    ПРИМЕР ВЫЗОВА

talk -b300 -t01 -l Обратиться к последовательному порту tty01 на скорости 300 бод и протоколировать выводные данные в текстовый файл.

    ТЕКСТ ПРОГРАММЫ

1 : 2 # @(#) talk v1.0 Talk to the serial port Author: Russ Sage Обращение к последовательному порту 4 BAUD="1200" 5 TTY="tty11" 6 PIPE="" 8 for ARG in $@ 9 do 10 case $ARG in 11 -b*) BAUD="`echo $ARG|cut -c3-`";; 12 -l) echo "logging in /tmp/talk.$$" 13 PIPE="| tee /tmp/talk.$$";; 14 -t*) TTY="tty`echo $ARG|cut -c3-`";; 15 -u) BAUD="9600";; 16 *) echo "talk: invalid argument $ARG" >&2 17 echo "usage: talk [-bBAUD] [-l] [-tTTY] [-u]" >&2 18 echo " -b baud rate" >&2 19 echo " -l log the output" >&2 20 echo " -t use another tty" >&2 21 echo " -u 9600 baud to UNIX" >&2 22 exit 1;; 23 esac 24 done 26 eval cu -s$BAUD -l$TTY dir $PIPE

    ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ

ARG Аргумент из командной строки BAUD Скорость обмена, которую нужно указать программе cu PIPE Содержит строку, создающую конвейер для захвата данных TTY Номер используемого порта tty ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ talk? Регистрация в удаленной системе расширяет горизонты ваших вычислительных возможностей. Учитывая, что UNIX становится все более распространенной системой среди микро-ЭВМ, системы UNIX, пригодные для связи, скоро будут везде. Кроме того, вы можете вызвать из вашей системы UNIX много систем типа досок объявлений, функционирующих на микро-ЭВМ. Нам нужен хороший интерфейс для управления модемной линией и облегчения ее использования. Нам необходимо захватывать данные, если это возможно, изменять скорость обмена и менять терминальный порт, если в этом есть потребность. Все это делает командный файл talk. ЧТО ДЕЛАЕТ talk? Программа talk генерирует командную строку, основанную на подразумеваемой конфигурации. Эта конфигурация такова: 1200 бод, подключение к терминальной линии /dev/tty11 (последовательный порт 1 в системе SCO XENIX), захват данных не производить. Все подразумеваемые параметры можно изменить в тексте исходного файла, а терминальная линия должна быть установлена с учетом специфики вашей системы. Если вы хотите изменить скорость обмена, укажите с помощью опции -b нужную вам скорость (подразумевается, что это одна из стандартных скоростей, поддерживаемых программой cu). Например,"talk -b2400" устанавливает скорость 2400 бод. Указанная скорость передается команде cu, которая непосредственно изменяет характеристики вашего подключения. Важно иметь в виду, что эта скорость должна быть определена в файле L-devices. Если вы хотите захватить все данные, выводимые на ваш экран, включите опцию протоколирования, указав -l. Файл с захваченными данными будет называться /tmp/talk.$$, где $$ - уникальный идентификатора процесса вашего текущего интерпретатора shell. Это имя выдается на экран при вызове данной опции, так что вы увидите его на вашем экране. Уникальность имен файлов обеспечивается для того, чтобы вы не потеряли по недосмотру регистрационные файлы от предыдущих сеансов работы (или чтобы, по крайней мере, было очень мало шансов, что это произойдет). Если у вас факультативный последовательный порт с модемом или если вы хотите подключаться к любой из возможных линий, примените опцию -t. Вместе с этой опцией укажите номер tty, который будет затем передан в команду cu. Это очень полезная опция, если у вас много различных линий для использования cu. Если вы не используете ваш последовательный порт для подключения модема, а имеете прямое подсоединение к другой системе UNIX, опция -u тотчас же установит скорость обмена 9600 бод. Заметим, что того же эффекта можно было бы достичь, указав -b9600, но опцию -u проще вводить и запоминать.

    ПРИМЕРЫ

1. $ talk -l -t12 Подключиться к дополнительному последовательному порту и захватывать данные, которые проходят на экране. 2. $ talk -u Подключиться к основному последовательному порту с использованием скорости 9600 бод. Не включать захват данных. 3. $ talk -b2400 -t04 -u Внимание! Сначала опция -b устанавливает скорость 2400, но далее следует опция -u, которая переустанавливает значение скорости на 9600. 4. $ talk -u -l -b4800 Снова будьте внимательны! Сначала линия подключается напрямую со скоростью 9600 бод. Затем включается протоколирование. Наконец, значение скорости меняется с 9600 на 4800. Если на самом деле линия, к которой вы обращаетесь, работает на скорости 9600 бод, вы должны, если это возможно, понизить скорость, нажимая ~%b для генерации сигнала break.

    ПОЯСНЕНИЯ

В строках 4-6 устанавливаются действия, выполняемые программой talk по умолчанию. Эти действия можно полностью изменить путем редактирования исходного файла. Главное достоинство этих умолчаний в том, что если такую конфигурацию вы используете чаще всего, то вам достаточно просто ввести "talk", без всяких опций. Строка 4 инициализирует переменную BAUD значением 1200, это значение по умолчание для скорости модема. Строка 5 присваивает переменной TTY значение tty11, что соответствует первому последовательному порту аппаратуры. Обычно нужно tty00, но SCO XENIX имеет виртуальную консоль, причем tty02-tty10 являются отдельными экранами, доступ к которым осуществляется с одного консольного устройства. Если это не так в вашей системе, укажите здесь правильное значение TTY. В строке 6 переменная PIPE инициализируется пустой строкой, так как конвейер применяется для протоколирования данных, а по умолчанию захват данных не выполняется. Строки 8-24 представляют собой цикл, который перебирает все аргументы командной строки. В строках 10-23 используется оператор выбора среди опций для выполнения требуемых функций. Строка 11 проверяет, начинается ли данная опция с символов -b, после которых следует что-то еще. Если да, то символы, следующие после -b, вырезаются и заносятся в переменную BAUD. Обратите внимание, что именно поэтому вы должны набирать "-b2400", а не "-b 2400". В строке 12 проверяется, хотите ли вы включить протоколирование. Если да, то на экран выводится имя протокольного файла, а переменная PIPE устанавливается так, чтобы направить стандартный вывод по конвейеру программе tee системы UNIX. Программа tee - это универсальная конвейерная программа расщепления, которую можно использовать для посылки копии потока данных в определенный пункт назначения, не воздействуя на основной конвейер. В данном случае мы применяем программу tee для посылки данных, проходящих через последовательный порт, на экран и в наш регистрационный файл /tmp/talk/.$$. (Если вы представите себе водяную трубу с T-образным стыком, то поймете, что происходит.) Строка 14 проверяет, начинается ли опция с -t. Если это так, то вырезается номер терминала. Здесь можно передать любое значение tty, но не забывайте, что этот номер tty должен также присутствовать в файле /usr/lib/uucp/L-devices. Благодаря этому файлу, программа cu знает, что терминальное устройство подключено к разрешенному порту. В строке 15 проверяется, собираетесь ли вы подключаться к системе UNIX напрямую. Если да, скорость обмена автоматически устанавливается на 9600 бод в целях повышения пропускной способности терминала. Строки 16-22 выполняют проверку ошибок. Если какая-либо опция, переданная в командной строке, не соответствует никакому из предыдущих образцов оператора case, этот факт фиксируется здесь, выдаются сообщения об ошибках и командный файл завершается. Строка 26 - это волшебная строка. Поскольку все shell-переменные получили свои значения, мы можем использовать их в командной строке, чтобы подставить в нее необходимые значения. Сначала применяется команда eval. Она раскрывает все имена переменных, заменяя их на соответствующие значения. В данном случае мы формируем полную командную строку утилиты cu: скорость берется из переменной BAUD, линия из TTY, а подсоединение прямое. Если мы не ведем протоколирование данных, значение PIPE нулевое и оно будет отброшено при синтаксическом разборе. Если же переменная PIPE содержит команду для отправки данных в протокольный файл, команда eval обеспечивает это, организовывая такой конвейер, как мы указали. ДОСТУП ИЗ UNIX В UNIX Теперь давайте рассмотрим полновесное общение двух систем UNIX. Имеется область, в которой UNIX опередила свое время. Система uucp позволяет связать несколько машин воедино и создать то, что в некоторых отношениях является виртуальным окружением, позволяющим вам работать на любой машине. Функционирование такой сети основано на удаленной регистрации в системе (cu(1)), дистанционном выполнении команд (uux(1)), электронной почте (mail(1)), передаче файлов (uucp(1), uucico(1)) и опознавании узла системы (uname(1) и uuname(1)). Поскольку мы занимаемся реализацией коммуникаций типа UNIX-UNIX, давайте рассмотрим некоторые способы физического соединения UNIX-машин в одну рабочую среду. СВЯЗЫВАНИЕ UNIX-МАШИН В рабочей обстановке машинные конфигурации постоянно меняются в целях тестирования, из-за аппаратных изменений, перестановок и переездов и т.д. Планировать конфигурацию ваших машин нужно так, чтобы она была максимально гибкой, это поможет избежать хаоса и разлада. Ваши потребности влияют на то, как вы соедините различные UNIX-машины. Если имеется блок определения приоритетного запроса к порту (port contender), вам приходится иметь с ним дело. Одни линии могут быть подсоединены напрямую, другие напрямую через блок опреде- ления приоритетного запроса к порту, третьи через модемные коммутато- ры. Для того чтобы научиться иметь дело со всеми этими возможностями, попробуем представить эти конфигурации в графическом виде. Первый тип подключения - прямое подключение (см. рис. 8-7). Слева показана вызывающая система, справа вызываемая. Вызывающая система использует последовательный порт для вывода, поэтому на этом порту не должно быть процесса getty. Инициирующая команда "cu -ltty00 -s9600 dir" обеспечивает подключение к последовательному порту tty00 на очень высокой скорости обмена. Прямые подсоединения могут поддерживать такую скорость. Сам кабель должен быть выполнен в виде нулевого модема (рассмотренного ранее в данной главе). В вызываемой системе задействованы процессы getty, работающие со скоростью 9600 бод на входящей терминальной линии. Когда пользователь вводит регистрационное имя, getty выполняет процесс login, запрашивающий пароль, и если этот пароль верный, запускается shell. Рис. 8-7. Прямое подключение одной системы UNIX к другой
DTE-1 DTE-2 +---------+ +---------+ | | | | | UNIX 1 | | UNIX 2 | | | Нулевой модем | | | tty00 |-------------------->| tty00 | | | | | | | | | +---------+ +---------+ ДЕЙСТВИЯ ДЕЙСТВИЯ 1. Нет getty 1. getty 9600 tty00 cu -ltty00 -s9600 dir login имя_пользователя sh 2. uucp файл 2. getty 9600 tty00 unix 2!~/user login uucp uucico
При использовании uucp происходят аналогичные вещи. Команда uucp генерирует процесс uucico, инициирующий вызов системы, показанной справа. Регистрационная последовательность та же самая, за исключением того, что вместо запуска интерпретатора shell в конце этой последовательности действий запускается еще один процесс uucico, который общается с вызывающим процессом. Следующая конфигурация - прямое подключение через селектор порта - показана на рис. 8-8. Здесь тоже кабель, идущий от DTE-1 к селектору порта, должен быть нулевым модемом. Рис. 8-8. Прямое подключение через селектор порта
+---------+ +----------+ +---------+ | | | | | | | UNIX 1 | | | | UNIX 2 | | | Нулевой | Селектор | Прямой | | | tty00 |--------->| порта |-------->| tty00 | | | модем | | кабель | | | | | | | | +---------+ +----------+ +---------+ ДЕЙСТВИЯ ДЕЙСТВИЯ 1. cu -ltty00 -s9600 dir 1. getty tty00 9600 login имя_пользователя sh 2. uucp файл 2. getty 9600 tty00 unix 2!~/user login uucp uucico
Селектор порта принимает любое количество входных линий и переключает их на меньшее количество фиксированных входных линий компьютера. Таким образом, можно получить доступ ко всем терминалам, не имея входной линии, которая часто бы пустовала, поскольку она предназначена определенному лицу. Регистрационная последовательность в точности такая же, как и для прямого подключения, кроме каких-либо дополнительных нажатий на клавиши, применяемых для прохождения через селектор. Обычно возврат каретки активизирует линию, чтобы получить регистрационную подсказку. Последняя конфигурация (на рис. 8-9) представляет собой дистанционное подключение двух систем UNIX. Каждое терминальное устройство DTE соединяется со своим модемом прямым кабелем. DTE-1 вызывает DTE-2 либо вручную с помощью cu, либо с помощью утилиты uucp, использующей uucico и программу набора телефонного номера. Самое большое отличие в том, что это подсоединение работает на скорости 1200 бод. Это значит, что либо DTE-2 запускает 1200-бодовый процесс getty, либо если getty имеет скорость 9600, то вам нужно сбросить ее. Для того чтобы понизить скорость, в программе cu требуется ввести ~%b в качестве сигнала break. Или, если вы работаете с помощью uucp, можно поместить строку BREAK в файле L.sys, чтобы отправить ее в вызванную систему. Рис. 8-9. Дистанционное соединение двух систем UNIX
+---------+ +---------+ | | | | | UNIX 1 | ***** _||_ ***** | UNIX 2 | | | Прямой * * _||_ * * Прямой | | | tty00 |------->* модем *-- || --* модем *------->| tty00 | | | кабель * * || * * кабель | | | | ***** || ***** | | +---------+ +---------+ ДЕЙСТВИЯ ДЕЙСТВИЯ 1. cu -ltty00 -s9600 dir 1. getty 1200 tty00 login имя_пользователя sh 2. uucp файл 2. getty 1200 tty00 unix 2!~/user login uucp uucico

    ОБЛАСТИ ДЛЯ ПЕРЕДАВАЕМЫХ ФАЙЛОВ

Когда вы пересылаете файлы между машинами, uucp придерживается определенных протоколов и стандартов. Один из них связан с тем, откуда и куда могут поступать файлы. Чтобы свести возможную неразбериху к минимуму и обеспечить определенную степень защиты, создаются защищенный и публичный каталоги для хранения стоящих в очереди заданий и переданных файлов. Наиболее важен каталог /usr/spool/uucp. Он содержит LOGFILE, что дает возможность с помощью команды "tail -f LOGFILE" заглянуть в операции передачи во время их выполнения. Транзакции uucp и mail попадают в этот каталог. Обычно транзакция состоит из управляющего файла (C.*) и файла данных (D.*). Когда одна машина используется в качестве центрального узла, ее каталог uucp может заполниться очень большим количеством файлов. Необходимо обычное сопровождение и постоянное слежение за файлами занятости (LCK* и STST*), чтобы быть уверенным, что все работает правильно. Следующий интересный каталог /usr/spool/uucppublic, чаще всего известный под названием PUBDIR (это shell-переменная). Он содержит каталоги, названные по имени каждого пользователя, чтобы хранить файлы, проходящие транзитом с одной машины на другую. Большинство каталогов имеют все права доступа, что обеспечивает другим пользователям возможность копировать файлы. Я посчитал полезным создать переменную среды интерпретатора shell, которая содержит маршрутный префикс моего каталога в /usr/spool/uucppublic, т.е. P=/usr/spool/uucppublic/russ. Теперь я могу обращаться к файлам, вводя $P/*. Это значительно сокращает ввод и упрощает пересылку файлов в этот каталог и из него. Переменная PUBDIR поддерживается синтаксисом uucp. В данном примере используется синтаксис uucp: $ uucp * remote!~/user Эта команда копирует каждый файл текущего каталога в систему "remote", затем обозначение ~/ превращается в префикс /usr/spool/uucppublic. Если в этой команде user не является каталогом, копируемые файлы получают имя user, а не копируются в каталог с именем user. Вы должны сами создать каталог, после чего применить к нему команду chmod 777, чтобы в него можно было копировать. Для справки отметим, что указание ^user превращается программой uucp в $HOME/user, а ^/user превращается uucp в $PUBDIR/user. Еще один способ ввода команды с помощью shell-переменных выглядит так: $ uucp * remote!$P Такая команда копирует все файлы в мой каталог PUBDIR. Если я зарегистрировался в этой системе, я могу ввести: $ ls -al $P или $ cd $P $ ls -al для того, чтобы увидеть все скопированные файлы. Один из моментов, за которым вы должны следить,- разрушительные командные файлы типа "uuclean". Эти программы обычно запускаются процессом cron или некоторыми другими фоновыми программами. Они проходят по всем областям системы, связанным с uucp, находят файлы, к которым не было обращений определенный период времени, и удаляют их. Это может быть катастрофическим, если вы используете PUBDIR в качестве временной области хранения. Вот как может выглядеть одна из таких "очищающих" операций: PATH=/usr/bin:/bin export PATH cd /usr/spool/uucp find C. D. TM. X. XTMP -type f -mtime +7 -exec rm {} \; cd /usr/spool/uucppublic find . -type f -mtime +7 -exec rm {} \; Если в вашей системе работают такие очищающие командные файлы, имеется несколько способов сохранения файлов от неумышленного уничтожения. Первый способ - постоянно обрабатывать все файлы утилитой touch, чтобы они не распознавались оператором find, который ищет старые файлы. Это можно сделать так: $ find $P -exec touch {} \; Утилита touch обновляет дату доступа и изменения файла. Фигурные скобки означают, что нужно поместить в них литеральное имя, соответствующее оператору find. Вам желательно оформить это в виде запланированного процесса, который запускается чаще, чем программа очистки! Другая стратегия - проанализировать очищающую программу (или командный файл). Она запускается суперпользователем (root)? Если нет, то команда "chmod 000 $P" может помешать ей вести поиск имен внутри моего каталога. Если же программа очистки запущена суперпользователем, то, конечно, никакие ограничения прав доступа ее не остановят. В какое время она запускается? Могу ли я приказать ей пройти мимо меня, не заглядывая в мои файлы? Что является стартовым каталогом для очищающего командного файла? Углубляясь в эти вопросы, мы можем собрать много информации о том, что делает этот командный файл и какие действия мы можем предпринять, чтобы он нам не навредил. Может показаться, что самый легкий способ - просто удалить программу очистки (в предположении, что вы имеете на это право). Это, однако, не способствует поддержке свободного пространства и чистых каталогов. К тому же если вы хотите запретить очистку каталогов, которые очень важны для вас, то вы должны нести ответственность за их сопровождение.

    СЕТЕВАЯ БЕЗОПАСНОСТЬ

Каталог uucppublic, как мы уже видели, содержит файлы, передаваемые транзитом между системами. Обычно все подкаталоги в uucppublic имеют режим доступа rwxrwxrwx. Это сразу же порождает проблему защиты, ведь кто угодно может создать, разрушить или изменить файлы в этих каталогах. Такие права доступа должны соблюдаться для любого каталога, который является источником или адресатом для файлов, передаваемых с помощью uucp. Uucp требует, чтобы все промежуточные каталоги имели разрешение на чтение и запись для всех пользователей. Если uucp берет из каталога исходные файлы, должны быть обеспечены права на чтение. Если же uucp записывает файлы в этот каталог, нужны права на запись в каталог-адресат. Если вы хотите, чтобы файлы uucp попадали прямо в ваш регистрационный каталог, вы должны разрешить запись в ваш каталог для всех окружающих. Широко открытый доступ на запись позволяет доставлять файлы прямо к вашему порогу, но вы не знаете, кому вы открываете двери. Если вы заботитесь о безопасности, вам не пон ПРИЕМЫ П. может записать все, что угодно, в ваше рабочее пространство. Одно из решений - разблокировать ваш регистрационный каталог, но заблокировать все ваши подкаталоги, кроме тех, которые нужны для uucp. Основная проблема при этом - так установить все права доступа, чтобы каждый файл обрабатывался надлежащим образом. Более простое решение - держать ваши каталоги для uucp за пределами вашего регистрационного дерева каталогов. Это избавляет вас от проблемы безопасности, но означает, что вы должны вручную копировать файлы после их попадания в каталог туда, где вы хотите их фактически разместить.

    ОГРАНИЧИТЕЛЬНЫЕ ФАЙЛЫ

Когда удаленная система регистрируется в центральной системе с помощью uucp, несколько файлов в центральной системе определяют, какие возможности имеет удаленная система. Эти конфигурационные файлы размещаются в каталоге /usr/lib/uucp. Первый из таких файлов называется L.cmds. Он содержит имена всех команд центральной системы, которые можно выполнить из удаленной системы. Если удаленная система посылает команду посредством uux, то команда выполняется только при условии, что имя этой команды присутствует в файле L.cmds. Следующий файл - USERFILE - определяет, к каким каталогам центральной системы может иметь доступ удаленная система. Вы можете ограничить пересылки одним каталогом или разрешить доступ к любому файлу системы. По умолчанию в файле USERFILE имеется такая запись: uucp, / что позволяет пользователю uucp (подразумеваемому пользовательскому имени процесса uucico) читать и писать файлы в любом месте дерева, начиная от корня и двигаясь вниз, т.е. фактически во всей системе. Это открывает лазейки в системе защиты, например: uucp central!/etc/passwd /tmp Здесь извлекается парольный файл из другой системы. С помощью этого файла можно найти имена пользователей без паролей, и другие люди могут прорваться в вашу систему. Более строгий файл USERFILE выглядит так: uucp /usr/spool/uucppublic /tmp что ограничивает файловые пересылки только указанными каталогами. Это препятствует предыдущей попытке пересылки парольного файла. Последний файл L.sys, вероятно, наиболее важен для uucp с точки зрения системы защиты. Он содержит имена узлов, телефонные номера, регистрационные имена и пароли для всех удаленных систем, известных центральной системе. Если бы какая-то часть этой информации была доступна широкой публике, кто-нибудь смог бы выполнить пересылку с помощью uucp из удаленной системы и претендовать на роль центральной системы. Новая система HoneyDanber uucp в System V кое-что делает для того, чтобы не давать удаленным системам перехватывать почту и пересылки данных путем маскировки под другие удаленные системы. В главе 9 более подробно рассматриваются вопросы безопасности программы uucp и коммуникаций вообще. В следующем примере показан файл L.sys, в котором определены два разных вида систем: системы типа прямого подключения и удаленные системы с номеронабирателем (dial-up systems). remote Any ACU 1200 5551212 ogin:--ogin: uucp word: uucp selector Any ACU 1200 5551213 \d--CLASS--CLASS A ogin:--ogin: uucp word: uucp direct Any tty00 9600 tty00 ogin:-@-ogin: uucp word: uucp Запись для системы "remote" отражает, что это линия с набором номера, может быть вызвана произвольное число раз ("any time"), доступна через ACU (automatic call unit - автоматическое устройство вызова) со скоростью 1200 бод и по номеру 5551212. Регистрационная последовательность определена в виде регистрационного имени uucp и пароля uucp. Слово "ogin" - не опечатка. Uucp использует подсказку "ogin:" для того, чтобы отличить ее от обычной подсказки login системы UNIX. Распознавание шаблона "ogin:" более надежно, чем использование "Login:" или "login:". Указание "Any" можно заменить на определенные интервалы времени, если 24-часовый доступ не разрешен. Заметим, что обозначение "ACU" соответствует записи ACU в файле L-devices (как рассмотрено ранее). В следующей записи файла L-devices имена cua0 и cul0 связаны с устройством, присоединенным к модему, в данном случае /dev/tty00: ACU cul0 cua0 1200 Эту связь можно проверить с помощью команды "ls -li /dev/tty* /dev/cul* /dev/cua*". Система "selector" в нашем файле L.sys также имеет набор номера, только подключается через коммутатор порта. Для общения с коммутатором порта необходима дополнительная информация, которая начинается с символов "\d". Поля в файле L.sys следуют в таком порядке: "ожидание посылка ожидание посылка ...". Когда мы в первый раз подключаемся к селектору порта, в нашу линию ничего не выводится. Просто он так работает. Селектору нужен символ , чтобы стать активным, а первое поле uucp означает ожидание. Как нам ничего не дождаться и послать возврат каретки? Путем указания uucp ожидать невозможный символ, например control-D (\d). Uucp никогда не получит его, поэтому по тайм-ауту эта программа выдаст возврат каретки (-). Если слово CLASS приходит назад, когда мы посылаем CR, то мы отправляем символ "A", который означает класс системной идентификации, указанный в данной записи. Когда связь устанавливается, мы ищем "ogin:" в качестве регистрационной подсказки. Если мы не находим ее, то посылаем символы возврата каретки. Это может потребоваться по той причине, что инициирующий процесс getty в системе A может быть настроен на 9600 бод. Тогда мы должны будем послать либо символы возврата каретки, либо символы break, чтобы сбросить скорость обмена до 1200 бод. Последняя системная запись "direct" не использует ACU и телефонный номер. Она собирается обращаться к терминальной линии tty00 на скорости 9600 бод, чтобы получить регистрационную последовательность. Здесь нет селектора порта, через который нужно пройти, а есть просто прямая линия. В файле L-devices эта линия описана так: DIR tty00 0 9600

    ОТЛАДКА КОММУНИКАЦИЙ

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

    НАЗНАЧЕНИЕ

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

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

uust Опции меню: c - подключиться к другой системе в отладочном режиме d - показать файлы в вашем каталоге PUBDIR f - длинноформатный список файлов в каталоге подкачки (spool directory) l - динамически отображать регистрационный файл r - повторное подключение к другой системе s - дать пользователю сводку о транзакциях u - разблокировать все терминальные линии (ОСТОРОЖНО: это может нарушить сеанс работы) w - отобразить регистрационный файл за последнюю неделю

    ПРИМЕР ВЫЗОВА

uust Динамически отображать регистрационный файл, чтобы следить за транзакциями uucp

    ТЕКСТ ПРОГРАММЫ

1 : 2 # @(#) uust v1.0 Uucp status utility Author: Russ Sage 4 if [ $# -gt 0 ] 5 then echo "uust: argument error" >&2 6 echo "usage: uust" >&2 7 exit 1 8 fi 10 UUNODE=`uuname -l` 11 echo " 12 UUST MENU system node: $UUNODE 13 --------- 14 c - connect with another system in debug mode 15 d - show files in your directory under PUBDIR 16 f - long list files in the spool directory 17 l - display the logfile dynamically 18 r - reconnect with another system 19 s - give user summary of transactions 20 u - unlock the tty line 21 w - display logfile for the last week 22 - exit program 24 Press c,d,f,l,r,s,u,w,or : \c" 25 read CMD 27 case $CMD in 28 "") exit 0;; 29 c) echo "\nSystem name ( to exit): \c" 30 read SYSTEM 31 if [ "$SYSTEM" = "" ] 32 then exit 0 33 fi 34 echo "\nrm /usr/spool/uucp/STST.$SYSTEM : \c" 35 rm /usr/spool/uucp/STST.$SYSTEM 2>/dev/null \ 36 && echo "" || echo "no STST files" 37 echo "\n/usr/lib/uucp/uucico -r1 -x4 -s$SYSTEM:" 38 /usr/lib/uucp/uucico -r1 -x4 -s$SYSTEM;; 39 d) echo "\n/usr/spool/uucppublic/$LOGNAME:" 40 ls -l /usr/spool/uucppublic/$LOGNAME;; 41 f) echo "\n/usr/spool/uucp:" 42 ls -l /usr/spool/uucp | more;; 43 l) echo "\n/usr/spool/uucp/LOGFILE:" 44 tail -20f /usr/spool/uucp/LOGFILE;; 45 r) echo "\nSystem name ( to exit): \c" 46 read SYSTEM 47 if [ "$SYSTEM" = "" ] 48 then exit 0 49 fi 50 echo "\nrm /usr/spool/uucp/STST.$SYSTEM : \c" 51 rm /usr/spool/uucp/STST.$SYSTEM 2>/dev/null \ 52 && echo "" || echo "no STST files" 53 echo "uusub -c$SYSTEM:" 54 if [ -f /xenix ] 55 then /usr/bin/uusub -c$SYSTEM 56 else /usr/lib/uucp/uusub -c$SYSTEM 57 fi 58 tail -20f /usr/spool/uucp/LOGFILE;; 59 s) echo "\nuulog -u$LOGNAME:" 60 uulog -un$LOGNAME | more;; 61 u) echo \\nrm /usr/spool/uucp/LCK* : 62 rm /usr/spool/uucp/LCK* 2>/dev/null || echo "no lock files";; 63 w) echo "\n/usr/spool/uucp/Log-WEEK:" 64 more /usr/spool/uucp/Log-WEEK;; 65 *) echo "uust: invalid argument '$CMD'" >&2;; 66 esac

    ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ

CMD Содержит символ входной команды, поступивший из стандартного ввода LOGNAME Содержит ваше регистрационное имя SYSTEM Узловое имя uucp той системы, в которую вы обращаетесь UUNODE Узловое имя uucp локальной системы ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ uust? Многие области системы UNIX являются фактически самостоятельными подсистемами. Uucp одна из них. Она использует конфигурационные файлы, файлы подкачки (spool files), файлы блокировки (lock files) и имеет много исполняемых модулей, образующих целую систему. Если вы интенсивно используете uucp, вы часто занимаетесь просмотром регистрационных файлов, очисткой каталогов после аварийных завершений работы uucp и наблюдением за системой в целом. Если делать все это вручную, требуется запоминать каталоги, файлы, команды, опции. Трудно запомнить так много символов для ввода команд. Uust значительно сокращает издержки и облегчает использование утилиты uucp. ЧТО ДЕЛАЕТ uust? Uust - это управляемый с помощью меню интерфейс, предоставляющий основные служебные функции, необходимые для среды uucp. Вам нужно набрать только команду uust, а затем требуемые опции. Вполне возможно также, что вы обнаружите трудосберегающие функции, о которых вы не знали раньше. Первое, что печатает данные командный файл - это главное меню:
| | UUST MENU system node: russ | --------- | c - connect with another system in debug mode | d - show files in your directory under PUBDIR | f - long list files in the spool directory | l - display the logfile dynamically | r - reconnect with another system | s - give user summary of transactions | u - unlock the tty line | w - display logfile for the last week | - exit program | | Press c,d,f,l,r,s,u,w,or : Первая опция 'c' предназначена для подключения линии uucp в отладочном режиме. Эта опция генерирует такую же командную строку, как при ручном способе запуска процесса uucico (см. предыдущий раздел). Uust запрашивает у вас имя системы, к которой вы хотите подключиться. Следующая опция 'd' дает длинноформатный листинг файлов вашего каталога, определяемого вашей переменной $PUBDIR, т.е. /usr/spool/uucppublic/$LOGNAME. Если вы часто делаете пересылки с помощью uucp, здесь может собраться много файлов. Данная опция предоставляет вам простой способ их просмотра. Опция 'f' выдает длинноформатный листинг всех файлов, находящихся в каталоге подкачки. Выход команды "ls -l" передается по конвейеру команде more, что удобно при наличии большого количества файлов. Этот каталог является сердцем исполняющей системы uucp. Почти все находится или в этом каталоге, или как-то связано с ним. Опция 'l', видимо, наиболее часто используемая во всей утилите. Она позволяет увидеть в динамике функционирование uucp, наблюдая за регистрационным файлом. При первом вызове она печатает последние 20 строк этого файла, а затем ведет наблюдение, пока не будет остановлена ее работа. Общее количество нажатий на клавиши для вызова этой команды с помощью uust равно 5: 4 для вызова uust и 1 для указания опции меню. Для того чтобы набрать ту же команду вручную, требуется 31 символ. Вот где экономия времени и избавление от лишних проблем! Очень интересна опция 'r'. Укажите ей имя системы - и она установит связь с этой системой при помощи uucp. Используемый при этом метод отличается от метода, применяемого в опции 'c', но работает в System V. Отметим, однако, что в большинстве систем команда uusub, задействованная здесь, требует возможностей суперпользователя. Uusub возвращает управление обратно вашему интерпретатору shell, но затем запускает uucp в фоновом режиме. Когда это произойдет, uust продвигается вперед и показывает динамический хвост регистрационного файла, как описано выше. После этого вы можете наблюдать все действия uucp по вызову, передаче файлов и завершению работы. Опция 's' показывает все ваши транзакции к утилите uucp, выбирая из регистрационного файла только то, что относится к вашему имени. Обычно имеется много записей, поэтому вывод передается по конвейеру команде more в целях удобства чтения. Следующую опцию 'u' нужно использовать с большой осторожностью. Она удаляет файлы блокировки, устанавливаемые системой uucp для самой себя. Если в это время uucp работает, другой пользователь может обратиться при помощи cu к линии и все разрушить. Эти файлы блокировки существуют также, когда кто-то применяет cu на последователной линии. Данная опция предусмотрена по той причине, что иногда uucp или cu завершается аварийно. Когда такое происходит, нужно удалить файлы блокировки, чтобы начать все сначала. Эту опцию следует использовать только для этой цели либо для запланированного эксперимента, чтобы посмотреть, что при этом может произойти. Последняя опция 'w' предназначена для отображения из регистрационного файла тех транзакций uucp, которые относятся к последней неделе. Это нетрудно сделать, но если бы вы искали определенную транзакцию, вы могли бы применить для этого средства поиска символьных строк команды more.

    ПРИМЕР

$ uust r Это попытка повторного подключения к линии uucp. Ее можно использовать для опроса другой системы или для отправки или приема данных, стоящих в очереди. Первым делом запрашивается имя системы, к которой нужно обратиться. Затем эта команда пытается удалить все файлы, оставшиеся после предыдущих аварийных завершений, и произвести вызов.

    ПОЯСНЕНИЯ

Uust является однопроходной утилитой, т.е. не имеет внутренних циклов. Вы выбираете опцию, и после завершения ее выполнения завершается весь командный файл. Таким способом это сделано главным образом по той причине, что вы должны обрывать вывод команды tail, коорая используется некоторыми опциями меню. Однако, клавиша break прекращает все, даже выполнение uust. Не имеет смысла делать внутренний цикл, так как этот цикл редко смог бы повториться. В строках 4-8 выполняется проверка командной строки на наличие ошибок. Если переданы какие-то аргументы, выдается сообщение об ошибке и командный файл завершается. Строка 10 инициализирует переменную UUNODE текущим именем uucp той системы, в которой вы работаете. Это обеспечивается вызовом утилиты uuname. Строки 11-24 отображают главное меню одним большим оператором echo. Имя системы, поступившее в переменную UUNODE, выдается для справки в правой верхней части экрана. В строке 25 читается ответ пользователя, а строки 27-66 проверяют команду и выполняют ее. Если вы ввели только возврат каретки, это соответствует строке 28, и программа завершается. Строки 29-38 обрабатывают команду 'c' - подключение в отладочном режиме. Запрашивается имя системы и проверяется, не пустое ли оно. Если оно пустое, командный файл завершается. В противном случае строка 34 сообщает, что uust пытается удалить все файлы STST, которые создавались при неудачных вызовах. Перед тем как обратиться к системе, вы должны удалить все файлы STST. Если таких файлов нет, команда rm не срабатывает и выдается сообщение "no STST files". Строки 39-40 выполняют опцию 'd'. Сначала отображается для справки название каталога, который мы просматриваем. Затем распечатывается каталог PUBDIR в длинном формате. Заметим, что LOGNAME соответствует любому пользователю, запустившему данную программу, поэтому нет необходиvости вписывать в текст программы какое-то значение. В строках 41-42 аналогичным образом обрабатывается опция 'f'. Мы распечатываем здесь каталог подкачки. Команда 'l' в строках 43-44 сообщает, что отображается регистрационный файл. Затем используется команда "tail -f". Опция -f означает, что нужно производить отображение того, что находится в файле следующим образом: как только транзакции печатаются в файл, они отображаются на ваш экран. Пользователь должен нажать клавишу break для выхода из программы. Строки 45-58 выполняют команду 'r', которая пытается произвести повторное подключение. Сначала запрашивается имя системы и сравнивается с пустой строкой. Если имя введено, мы пытаемся удалить файлы STST и выдаем сообщение о том, было ли это удаление успешным. Затем печатается сообщение о том, что запускается процесс uusub. В строке 54 проверяется, является ли корневым файлом XENIX. Если это так, то утилита uusub вызывается с учетом ее нового местонахождения в системе XENIX. В противном случае она вызывается с указанием ее обычного местонахождения - /usr/lib/uucp. После выполнения uusub отображается динамический хвост регистрационного файла, пока пользователь не нажмет клавишу break, после чего командный файл завершается. В строках 59-60 выполняется проверка сводки о транзакциях. Используется команда uulog, ей передается LOGNAME текущего пользователя. Подробности о команде uulog можно найти в документации. Опция 'u' рассматривается в строках 61-62. Сначала отображается команда rm, которая будет выполнена, чтобы уведомить об этом пользователя. Затем эта команда выполняется, чтобы попытаться удалить файлы блокировки. Если команда удаления завершается неудачей, выдается сообщение об ошибке, указывающее на отсутствие файлов блокировки. Строки 63-64 запускают опцию 'w', чтобы посмотреть деятельность uucp за последнюю неделю. С помощью команды more печатается файл LogWEEK. Этот файл создается утилитой uucp, чтобы обеспечить сжатую сводку о работе в течение недели. Строка 65 соответствует любым командам, которые не были распознаны ранее. Такие команды являются ошибочными, и выдается сообщение об этом. Когда выполнение программы доходит до этого места, она завершается.
ИМЯ: uutrans
uutrans Передача файловых деревьев из одной системы UNIX в другую систему UNIX

    НАЗНАЧЕНИЕ

Копирует полную иерархию файловой системы с помощью uucp в другую систему UNIX и поддерживает структуру файлового дерева.

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

uutrans

    ПРИМЕР ВЫЗОВА

cd $HOME/backup uutrans remote ~russ Начиная с подкаталога backup моего регистрационного каталога, запустить uutrans с целью передачи всех моих файлов. Файлы посылаются в систему remote и размещаются там в моем регистрационном каталоге (/usr/russ).

    ТЕКСТ ПРОГРАММЫ

1 : 2 # @(#) uutrans v1.0 Transfer file trees via uucp 3 # Author: Russ Sage 4 if [ $# -gt 0 ] 5 then echo "uutrans: argument count error" >&2 6 echo "usage: uutrans" >&2 7 exit 1 8 fi 10 SELF=`uuname -l` 12 echo "source directory( to exit): \c" 13 read SOURCE 14 if [ "$SOURCE" = "" -o ! -d "$SOURCE" ] 15 then exit 1 16 fi 18 echo "\ndestination system( for $SELF): \c" 19 read SYSTEM 20 echo "\ndestination directory( for ~/$LOGNAME): \c" 21 read DEST 23 : ${SYSTEM:="$SELF"} 24 : ${DEST:="~/$LOGNAME"} 26 echo "\nQUEUEING:" 28 find $SOURCE -type f -print | sort | while read FILE 29 do 30 echo $FILE 31 uucp -c -d -r $FILE $SYSTEM!$DEST/$FILE 32 done

    ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ

DEST Место назначения, куда нужно копировать файлы FILE Определенный копируемый файл LOGNAME Содержит регистрационное имя SELF Содержит узловое имя текущей системы SOURCE Источник всех копируемых файлов SYSTEM Имя системы, в которую нужно копировать ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ uutrans? Копирование файлов из одной системы в другую с помощью uucp - довольно простая задача: достаточно указать, какие файлы куда нужно направить. Но как быть в случае, когда вам нужно скопировать иерархию файлов? Как гарантировать, что файлы расположатся в системе-адресате в виде иерархии, а не будут засунуты в один и тот же каталог? Единственное предложение в документации по uucp (даже не на страницах руководства) говорит о том, как это сделать. Если вы никогда не читали его, то скорее всего ваши пересылки завершатся неудачей. Нам нужно инструментальное средство, которое передает древовидные структуры в другие системы, сохраняя при этом форму этих структур. Мы хотим обеспечить простой способ выполнения этой работы, чтобы не искать каждый раз припрятанную информацию. ЧТО ДЕЛАЕТ uutrans? Uutrans предоставляет гибкий интерфейс, гарантирующий правильное копирование файловых иерархий. Этот командный файл знает, какие файлы передавать, в какую систему их направить и где их разместить в этой системе. Когда вы запускаете uutrans, первым делом он запрашивает исходный каталог для пересылки файлов. Если вы не хотите продолжать, нажмите возврат каретки для выхода. Обратите внимание, что используется команда find, поэтому путь, который вы указываете в ответ на подсказку, является префиксом маршрутного имени для всех передаваемых файлов. Например, если вашим текущим каталогом является $HOME и вы хотите сделать его каталогом-источником, введите ".". Оператор find сгенерирует для файла /src/f.c маршрутное имя ./src/f.c. Если же вы введете, например, имя /usr/russ, то результирующим маршрутным именем будет /usr/russ/src/f.c. Таким образом, требуется небольшая предусмотрительность, чтобы избежать создания лишних уровней каталога в системе-адресате. Затем запрашивается имя системы-адресата. Если вы хотите, чтобы это была ваша собственная система, нажмите возврат каретки. Это позволит скопировать файлы в пределах того же жесткого диска или в другие файловые системы. В запросе каталога-адресата умолчанием является PUBDIR. Если вам это не подходит, укажите точное маршрутное имя каталога, с которого вы хотите начать иерархию в системе-адресате. (Напомним также, что многие системы накладывают ограничения на то, куда вы можете копировать файлы с помощью uucp.) Способ, которым uutrans поддерживает правильное файловое дерево - это указание абсолютного маршрутного имени в системе-адресате, ниже которого будут располагаться все передаваемые файлы. Единственный способ сделать это - занести маршрутное имя в переменную и использовать ее в качестве путей как источника, так и адресата при вызове uucp. Когда вся входная информация введена, используется команда find, чтобы найти все файлы, относящиеся к данной иерархии. Затем каждое маршрутное имя передается по конвейеру в цикл, который отображает имя и передает файл адресату при помощи uucp. Для того чтобы ускорить весь процесс и сэкономить свободное пространство, файлы ставятся в очередь в каталоге подкачки (т.е. не делается попытка вызвать удаленную систему), и никакие файлы не копируются в каталог подкачки. Это экономит время, которое ушло бы на копирование больших количеств файлов, и даже может обезопасить вашу машину от аварийных завершений работы, так как копирование большого числа файлов забирает все свободное пространство. Когда все файлы поставлены в очередь, вам нужно вручную подсоединиться к uucp с помощью команды 'c' или 'r' утилиты uust. Все каталоги в системе-адресате должны создаваться автоматически по мере пересылки файлов. Несколько неприятный вопрос - права собственности и доступа к файлам на принимающей стороне. Обычная последовательность действий такова: 1. Поставить файлы в очередь к uucp, используя инструментальное средство uutrans. 2. Выйти из системы-источника. 3. Войти в систему-адресат. 4. Запустить uust и повторно подключиться к uucp. Если вы придерживаетесь такой технологии постановки в очередь и передачи, то вы полностью контролируете, какие файлы куда направляются. Имеется побочный эффект, заключающийся в том, что права доступа к файлам в каталоге-адресате устанавливаются в соответствии с маской интерпретатора shell (shell umask) того процесса, который производит повторное подключение. Необходимо отметить, что если у вас нет прав доступа на запись в вашей umask, то создается пересылочный каталог, но в него не могут быть помещены никакие файлы, что подрывает передачу файлов с помощью uucp. Для разрешения этой проблемы измените вашу umask на "000" (по умолчанию она равняется 777 для каталогов и 644 для обычных файлов).

    ПРИМЕРЫ

1. cd $P uutrans . remote Сменить каталог на PUBDIR. Передать все файлы из текущего каталога (.) в удаленную систему, размещая их в подразумеваемом каталоге ~/$LOGNAME, т.е. в моем каталоге PUBDIR. 2. uutrans /etc remote /tmp/etc Передать все файлы из каталога /etc в систему remote, помещая их в каталог /tmp/etc.

    ПОЯСНЕНИЯ

Строки 4-8 проверяют наличие ошибок. Если в командной строке есть аргументы, печатается сообщение об ошибке. Строка 10 инициализирует переменную SELF именем вашего системного узла, используемого утилитой uucp. Строки 12-16 запрашивают каталог-источник и читают ответ в переменную SOURCE. Если ничего не введено или указан не каталог, программа завершается. Строки 18-21 запрашивают систему-адресат и каталог-адресат. Умолчаниями являются ваша собственная система и каталог PUBDIR для размещения файлов. Эти умолчания облегчают конфигурирование, связанное с пересылкой информации, чтобы не было необходимости вводить много данных. В строках 23 и 24 проверяется, инициализированы ли переменные. Если переменной SYSTEM присвоен возврат каретки, в нее заносится значение SELF. Если переменная DEST пустая, ей присваивается ~/$LOGNAME. Эти операторы устанавливают значения по умолчанию. Строка 26 печатает сообщение о том, что файлы ставятся в очередь. Весь фокус заключается фактически в строках 28-32. Команда find начинает работать с каталога SOURCE и находит все обычные файлы. Этот список передается утилите sort, так что uucp пересылает файлы в отсортированном порядке. Это облегчает отслеживание того, какие файлы были переданы, если возникают какие-то проблемы. Отсортированный список попадает в цикл while, читающий имена файлов. Каждое имя отображается на экран для справки, и файл ставится в очередь к удаленной системе. В строке 31 указаны такие опции uucp: -r для постановки в очередь, -c для того, чтобы не копировать файлы в каталог подкачки, и -d для создания каталогов, необходимых в удаленной системе. Для поддержки древовидной структуры мы применяем магический синтаксис $SYSTEM!$DEST/$FILE. Нам нужно не только скопировать файлы в систему и в каталог, но и определить маршрутное имя вплоть до самого файла. Это гарантирует, что файлы будут расфасованы по каталогам. Если бы был использован синтаксис $SYSTEM!$DEST, то все файлы попали бы в один каталог и их иерархическая структура была бы утеряна. После того как все файлы поставлены в очередь, они находятся в этой очереди до тех пор, пока их не позовет удаленная система. Тогда файлы будут скопированы в эту систему.

    КОНФИГУРАЦИОННЫЕ РЕШЕНИЯ

Зачастую на практике вокруг нашей системы UNIX накапливается много не особенно совместимой аппаратуры, включая терминалы, микро-ЭВМ и модемы. Как заставить их работать совместно? Теперь когда мы рассмотрели основные особенности аппаратуры и программмного обеспечения для коммуникаций в системе UNIX, мы готовы представить некоторые примеры работы коммуникаций в комплексах с системой UNIX. Мы хотим рассмотреть ряд конфигураций, использующих различные комбинации аппаратуры. Проблемы меняются, но наш общий подход остается. ПОДКЛЮЧЕНИЕ UNIX К МОДЕМУ И ГЛАВНОЙ МАШИНЕ Первая задача, которую мы рассмотрим, это использование "персональной" системы UNIX, модема и главной машины. У нас есть система UNIX с одним последовательным портом, но мы имеем как модем, так и линию прямого подключения к системе большего размера. Как нам использовать один последовательный порт и для модема, и для главной машины? Решение показано на рис. 8-10. Рис. 8-10. Подключение системы UNIX к модему и главной машине
***** +----------------+ * * | | * модем * | главная машина | * * | | ***** +----------------+ ^ ^ | Прямой кабель | Нуль-модем | | | +---------------+ | ------>| |------- | переключатель | | | \ / \___________/ ^ | | Прямой кабель | v +-------------+ | | | UNIX | | | +-------------+
Нам нужен блок переключения. Этот блок имеет один вход на одной стороне и два входа на другой. Мы подсоединяем UNIX к главному входу, что позволяет переключаться либо на модем, либо на главную машину. Здесь два различных подсоединения: UNIX <-- --> модем Вызов из UNIX при помощи cu, uucp и обращение в UNIX с удаленного терминала UNIX --> главная машина UNIX обращается к главной машине, как прямо подключенный терминал ПОДКЛЮЧЕНИЕ UNIX И ТЕРМИНАЛА К МОДЕМУ Эта конфигурация аналогична предыдущей, за исключением того, что вся аппаратура локальна. Мы хотим использовать модем как для терминала, так и для UNIX. Конфигурация показана на рис. 8-11. Порт системы UNIX подсоединяется к одной стороне переключателя, а терминал к другой. Отметим, что терминал НЕ будет обращаться в систему UNIX. Все кабели прямолинейные. В таком комплексе возможны следующие конфигурации: UNIX <-- --> модем Обращение в UNIX с удаленного терминала и вызов из UNIX с помощью cu, uucp терминал --> модем Для вызова из UNIX только в качестве немого терминала Рис. 8-11. Подключение UNIX и терминала к модему
***** * * * модем * * * ***** ^ | | Прямой кабель | v /-----------\ / \ | | | переключатель | | | ------->| |<------- | |_______________| | Прямой | | Прямой кабель | | кабель | | v | +-----------+ +--------------+ | | | | | UNIX | | терминал | | | | | +-----------+ +--------------+
ПОДКЛЮЧЕНИЕ UNIX К ТЕРМИНАЛУ, МОДЕМУ И ГЛАВНОЙ МАШИНЕ Эта ситуация довольно сложна и требует двух блоков переключения. Соответствующая архитектура показана на рис. 8-12. Рис. 8-12. Подключение системы UNIX к терминалу, модему и главной машине
***** +----------------+ * * | | * модем * | главная машина | * * | | ***** +----------------+ ^ ^ Прямой | | Нуль- кабель | | модем | +---------------+ | +-------------+ ----->| |----- | | | переключатель | | терминал | | 2 | | | \ / +-------------+ \___________/ | ^ | Нуль-модем | Прямой кабель | | | +---------------+ | ------>| |<------ | переключатель | | 1 | \ / \___________/ ^ | | Прямой кабель | v +-------------+ | | | UNIX | | | +-------------+
Начнем с системы UNIX. В ней имеется линия, идущая в переключатель 1 в качестве главного входа. Переключатель 1 осуществляет переключение между терминалом и блоком переключения 2. Это позволяет системе UNIX либо получать терминал для регистрации в системе (в предположении, что работает getty), либо получать модем для регистрации в системе, либо посылать информацию модему или главной машине (предполагается, что процесса getty нет). Заметим, что между терминалом и UNIX должен быть нулевой модем, но мы не можем сделать нуль-модем сразу после UNIX, потому что нам нужен прямой кабель для подсоединения к модему. Затем блок переключения 2 выполняет коммутацию между модемом и главной машиной. Линия к модему должна быть прямой, а линия в главную машину должна быть нуль-модемом. Возможны следующие взаимосвязи: UNIX <-- терминал Обращение в UNIX с терминала по линии getty UNIX <-- --> переключатель <-- --> модем Вызов из UNIX с помощью cu, uucp и обращение в UNIX с удаленного терминала UNIX --> переключатель --> главная машина Обращение из UNIX при помощи cu, uucp СИСТЕМА UNIX, МИКРО-ЭВМ И МОДЕМ В этой конфигурации также требуется два блока переключения. У нас есть машина с двумя последовательными линиями, на которой работает UNIX, автономная микро-ЭВМ и модем, который они должны совместно использовать. UNIX-машина также воспринимает регистрацию с автономной машины, позволяя ей эмулировать терминал, если у нас есть соответствующее программное обеспечение. Решение показано на рис. 8-13. Рис. 8-13. Система UNIX, микро-ЭВМ и модем
***** * * * модем * * * ***** ^ | прямой кабель | v ______________ / \ / \ | переключатель | | 2 | ------------------- ^ ^ | | | | ----------------- ------------------- | | | ------------------------------- | | | | | | | | | | | ------------------- | | | | | | | переключатель | | | \ 1 / | | \_____________/ Прямой | | Нуль-модем ^ кабель | | | v v | +--------+ +-------------+ | | | | | UNIX | | микро-ЭВМ | | | | | +--------+ +-------------+
Микро-ЭВМ может работать двумя способами: либо как терминал в системе UNIX, либо как терминал для обращения к модему. Система UNIX имеет один последовательный порт, позволяющий связываться с микро-ЭВМ через переключатель 1. Другая последовательная линия идет в блок переключения 2, подсоединенный к модему. По этой линии можно либо обращаться из системы UNIX во внешний мир, либо принимать внешние вызовы с целью удаленной регистрации в системе через модем. Переключатель 2 коммутирует модем между микро-ЭВМ и системой UNIX. Возможны такие комбинации: микро-ЭВМ --> переключатель --> модем Обращение вовне с помощью коммуникационных средств микро-ЭВМ --> переключатель --> UNIX Регистрация в системе путем эмуляции терминала UNIX <-- переключатель <-- микро-ЭВМ Регистрация с терминала, подключенного напрямую UNIX <-- --> переключатель <-- --> модем Вызов из UNIX при помощи cu, uucp, удаленная регистрация модем <-- переключатель <-- микро-ЭВМ Вызов модема с микро-ЭВМ модем <-- --> переключатель <-- --> UNIX Вызов из UNIX или обращение в UNIX; требуется изменение getty для последовательной линии и автоответа модема

    АЛЬТЕРНАТИВНОЕ РЕШЕНИЕ

Возможно еще одно решение предыдущей задачи. Оно использует несколько больше возможностей, чем предыдущее решение. Те же основные проблемы решены, но подход при этом сильно отличается. В конфигурации, показанной на рис. 8-14, блоки переключения подсоединяются иначе, чем в предыдущем комплексе. Доступны те же пути передачи информации, а также некоторые новые. Рис. 8-14. Система UNIX, микро-ЭВМ и модем в альтернативной конфигурации
***** * * ------->* модем * | * * -------------------- | ***** | | | | | | прямой кабель | | v | _______________ | | | | нулевой | переключатель | | модем \ 2 / | \-----------/ | ^ | | прямой кабель | | | v | /-----------\ | / \ | | переключатель | | | 1 | | +_______________+ | ^ ^ | | | | прямой кабель | | прямой кабель | ------------------- -------------- | | | v v | +--------+ +---------------+ | | | | | UNIX | | микро-ЭВМ | | | | | +--------+ +---------------+
Как микро-ЭВМ, так и система UNIX подключены к переключателю 1. Этот переключатель присоединен к блоку переключения 2 главным входом. Линии от микро-ЭВМ, от UNIX, между переключателями и модемная линия должны быть прямыми кабелями. Только входная линия системы UNIX должна быть нуль-модемом. Переключатель 2 коммутирует один из центральных процессоров между модемом и второй последовательной линией системы UNIX. Модемная линия может идти от модема или к нему, но линия системы UNIX должна быть только входящей, потому что в UNIX работает getty. Возможны такие комбинации: микро-ЭВМ --> переключатель --> модем Обращение вовне при помощи коммуникационных средств микро-ЭВМ --> переключатель --> UNIX Регистрация в системе путем эмуляции терминала UNIX <-- --> переключатель <-- --> модем Обращение из или в UNIX с помощью cu, uucp UNIX --> переключатель --> UNIX Выход с первой линии идет во вторую линию для регистрации в системе модем <-- --> переключатель <-- --> процессор Выбор центрального процессора зависит от переключателя 1 UNIX <-- переключатель <-- процессор В зависимости от переключателя 1, один из процессоров может зарегистрироваться в системе ТРИ СИСТЕМЫ UNIX Последняя конфигурация, которую мы рассмотрим, это соединение трех систем UNIX. Данное решение было подсказано конфигурацией с прямым подсоединением 16-разрядной машины PC XENIX, 32-разрядной машины PC XENIX и главной машины VAX. Для того чтобы выполнить такое подключение, нам нужно три двунаправленных блока переключения. Не так важно, какая из микро-ЭВМ 1 и микро-ЭВМ 2 является 32- или 16-разрядной машиной. Они могут быть в вашем помещении, а главная машина может стоять где-то в другом месте и соединяться кабелем с вашим помещением. При этом предполагается, что кабель от главной машины уже является нуль-модемом в вашем подключении. Еще один нулевой кабель располагается между двумя системами UNIX на микро-ЭВМ, чтобы можно было зарегистрироваться с одной из этих систем в другой системе в качестве терминала. Остальные кабели прямые (см. рис. 8-15). Рис. 8-15. Три системы UNIX
+------------------+ | | | UNIX | | главная машина | | | +------------------+ ^ | нуль-модем | ______________ / \ / \ | переключатель | | 3 | |__________________| ^ ^ | | прямой кабель | | прямой кабель ---------------- ---------------- | | | ----------------------- | | | нуль-модем | | | v v | --------------- --------------- | | | | | переключатель | | переключатель | \ 1 / \ 2 / \___________/ \___________/ ^ ^ прямой | | прямой кабель | | кабель v v +-------------+ +-------------+ | | | | | UNIX | | UNIX | | Микро-ЭВМ 1 | | Микро-ЭВМ 2 | | | | | +-------------+ +-------------+
Каждая микро-ЭВМ подсоединена к блоку переключения через главное соединение. Эти переключатели 1 и 2 коммутируются друг с другом и с блоком переключения 3. Микро-ЭВМ 1 может переключаться на микро-ЭВМ 2 в качестве интерфейса терминал/uucp и на главную машину в качестве удаленного терминала. В этом случае линия главной машины проходит через селектор порта. Микро-ЭВМ 2 может переключаться между микро-ЭВМ 1 и главной машиной аналогичным образом. Переключатель 3 коммутирует главную машину между микро-ЭВМ 1 и микро-ЭВМ 2. Возможны следующие комбинации: микро-ЭВМ 1 --> переключатель --> главная машина Зарегистрироваться в качестве удаленного терминала микро-ЭВМ 1 <-- --> переключатель <-- --> микро-ЭВМ 2 Обращение в UNIX или из UNIX в зависимости от getty микро-ЭВМ 2 <-- --> переключатель <-- --> микро-ЭВМ 1 Обращение в UNIX или из UNIX в зависимости от getty микро-ЭВМ 2 --> переключатель --> главная машина Зарегистрироваться в качестве удаленного терминала главная машина <-- переключатель <-- микро-ЭВМ 1 Зарегистрироваться с удаленного терминала главная машина <-- переключатель <-- микро-ЭВМ 2 Зарегистрироваться с удаленного терминала *** Вероятно, рассмотренные нами конфигурации могут не в точности соответствовать вашим потребностям. Однако мы представили их с той целью, чтобы вы увидели разнообразие типовых решений, которые вы можете адаптировать для себя. В следующей главе мы несколько отойдем от всех этих гаек и болтиков и подробнее рассмотрим вопросы, связанные с администрированием системы, особенно вопросы безопасности.

    * ГЛАВА 9. Администрирование и безопасность *

ОБЯЗАННОСТИ И ОТВЕТСТВЕННОСТЬ АДМИНИСТРАТОРА ПОДДЕРЖКА БЕЗОПАСНОСТИ В СИСТЕМЕ ТИПИЧНЫЕ ПРОБЛЕМЫ БЕЗОПАСНОСТИ access показ всех беспарольных входов в систему chkset проверка наличия в системе файлов с разрешенной установкой идентификатора пользователя или группы suw отслеживание нарушителей по протокольному файлу команды su

    АДМИНИСТРИРОВАНИЕ И БЕЗОПАСНОСТЬ

    ЗАЧЕМ НАМ ИЗУЧАТЬ АДМИНИСТРИРОВАНИЕ

----------------------------------- Вашу карьеру в системе UNIX в качестве ученика (стать бы поско- рее мастером!) можно представить себе в виде трех ступеней квалифика- ции. Первая ступень - посвящение в работу системы UNIX в целом, осо- бенно изучение ее сердцевины - файловой системы. Первые три главы заложили этот фундамент и предоставили практические инструментальные средства для обслуживания системы. Вторая ступень мастерства - под- держка вашей собственной работы и персональной среды - рассматривает- ся в главах с четвертой по шестую. В главах 7 и 8 более подробно рассматриваются два специальных аспекта практической работы с систе- мой UNIX - устройства и коммуникации. Теперь мы готовы достичь третьей ступени мастерства - курировать работу самой системы, что более прозаически называется системным ад- министрированием. Если вы в настоящий момент такой пользователь, который занимает- ся программированием, или если вы все время работаете системным прог- раммистом, то вас может удивить, зачем вам становиться на точку зре- ния системного администратора и овладевать его основными инструментами работы. На это имеется две серьезные причины: знание и необходимость. Системное администрирование требует близкого знакомства с тем, где и что находится в системе, и понимания взаимосвязи конкретного процесса с системой в целом. Программисты стремятся нахвататься све- дений о чудесах и результатах таинственных команд и о всяческих хит- ростях, которые они считают полезными, но зачастую они не хотят тра- тить время на знакомство с системой UNIX в целом. Мы бы хотели сагитировать вас на более систематическое изучение системы UNIX с той целью, чтобы вы могли открыть для себя новые кладовые знаний. Вот по- чему на протяжении данной книги мы создавали инструментальные средс- тва, которые не только делают полезные вещи, но и помогают вам изу- чать саму систему. Необходимость знать администрирование становится ощутимой, когда вы обнаруживаете, что вам вдруг задали работу системного администра- тора или администратор ушел в отпуск, что пользователи выстроились возле ВАШЕГО стола в ожидании помощи, поскольку вас считают признан- ным авторитетом. Другой причиной возникновения необходимости может быть то, что вы стали безраздельным хозяином вашей собственной микро- ЭВМ с системой UNIX и хотите все установить и поддерживать по своему вкусу. Взгляните на такую необходимость администрирования как на воз- можность накопить обширные и доскональные знания о UNIX, которые мо- гут сослужить вам добрую службу в вашей будничной работе с компьюте- ром. Быть мастером UNIX - дело чести и технической грамотности. Пыта- ясь удовлетворить требованиям необходимости, мастера UNIX учатся пос- певать за потоком необходимых им знаний. Мастера UNIX не только хоро- шо играют, но и просчитывают игру на один ход вперед.

    НЕКОТОРЫЕ НАБЛЮДЕНИЯ ПОСВЯЩЕННОГО В АДМИНИСТРИРОВАНИЕ

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

    АДМИНИСТРАТИВНЫЕ ОБЯЗАННОСТИ

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

    ПОДДЕРЖКА РАБОТОСПОСОБНОСТИ СИСТЕМЫ

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

    УЛУЧШЕНИЕ СИСТЕМНЫХ ВОЗМОЖНОСТЕЙ

По мере того, как все больше и больше людей эксплуатируют систе- му, требуется все больше ресурсов, но вы можете также найти способы использования имеющихся ресурсов с большей эффективностью. Разрабаты- вая способы повышения производительности системы, вы найдете для себя много полезного в инструментальных средствах, представленных в главе 3. В системе UNIX редко хватает принтеров, дисковых устройств, после- довательных портов, сетевого оборудования и т.п., но более эффектив- ное применение может помочь решить те же задачи без добавления новых ресурсов. По всей видимости, наиболее важным ресурсом является время цент- рального процессора. Посадите тридцать пользователей в системе, расс- читанной на двадцати четырех - и вы сразу почувствуете нехватку про- цессорного времени. В работу администратора входит поддержка текущих ресурсов, а также планирование роста системы на будущее. Поэтому тре- буется, чтобы вы знали типичные раскладки использования вашей систе- мы, знали, где могут возникнуть "узкие места", как эффективно распре- делить имеющиеся ресурсы и какие способы наращивания системы могут быть наиболее эффективными по стоимости. Некоторую полезную информа- цию можно извлечь путем "статического" инспектирования файловой сис- темы с помощью средств, представленных в главе 2. Более динамичную картину эксплуатации системы вы можете получить, применяя команду ps, чтобы увидеть, какие процессы активны в настоящее время, и применяя команду w (в системе Berkeley), которая выдает статистику загрузки системы и организации очередей.

    ПОМОЩЬ ПОЛЬЗОВАТЕЛЯМ

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

    БЕЗОПАСНОСТЬ: НУЖЕН СТОРОЖЕВОЙ ПЕС

Администратор ведает всеми вопросами безопасности. Обычно он единственный, кто занимается этой работой. Пользователи не обеспечи- вают безопасность, потому что они не умеют или не знают, как это сде- лать. Нарушители безопасности могут атаковать систему многими спосо- бами. Они могут занять системные ресурсы, заполняя таблицу процессов или таблицу открытых файлов и распределяя для себя все свободное дис- ковое пространство и все свободные индексные дескрипторы файлов. Они могут перепутаться с другими пользователями системы или поменять сис- темное время. Они могут повредить файлы данных или исполняемые модули и даже подделать почту. Но при вашей организации системы они чаще всего становятся просто "пользователями-хулиганами". Позже мы расс- мотрим более серьезные вещи, чем системное хулиганство - тех пользо- вателей, кто может несанкционированно воспользоваться правами супер- пользователя. Наилучший подход к проблемам безопасности - осознать, что дейс- твительно нуждается в защите, а не пытаться стать суперсыщиком. Вы должны особенно заботиться о нуждах системных программистов и других опытных авторитетов в вашей системе. В идеале администратор должен работать ВМЕСТЕ, а не против признанных авторитетов и поддерживать с ними хорошие деловые контакты. Не у всех нарушителей одинаковые моти- вы. Например, кто-то из авторитетов может захотеть получить доступ к правам суперпользователя для того, чтобы самостоятельно делать неко- торую работу, а не ждать, когда ее сделаете вы. Кто-то другой может быть обижен на кого-то или вообще на весь мир и стремится отомстить путем разрушения файлов. Каждая ситуация требует индивидуальной оцен- ки. Напомним, что разделяющая линия между администраторами и систем- ными авторитетами зачастую неопределенная и непостоянная. Безопас- ность иногда превращается в игру, в которой определенные пользователи пытаются разглядеть, чего они могут избежать, а администраторы пыта- ются сохранить контроль над системой. В работе сторожевого пса задействовано пять функций: 1. Защита от неразрешенных входов в систему, файлов, программ и команд su. 2. Сохранение конфиденциальности определенных данных. 3. Наблюдение за использованием модемов. 4. Предотвращение неразрешенных пересылок файлов. 5. Сведение к минимуму возможностей взлома. Здесь много работы! Система UNIX так обширна, и файлы могут скрываться в таком большом количестве мест, что один лишь поиск са- мозванцев занимает почти все время. Это требует от администратора не столько тяжелой, сколько изобретательной работы. Для того чтобы обес- печить себе шансы на победу в этой борьбе, вы должны сделать так, чтобы система помогала себя защищать. Несколько позднее в этой главе мы предложим инструментальные средства access и suw, которые помогают вам защищаться от запрещенных входов в систему, и командный файл chkset, имеющий дело с защитой конфиденциальных файлов. Мы также предлагаем более детальный взгляд на конкретные проблемы безопаснос- ти.

    ЗАЩИТА ОТ ЗАПРЕЩЕННЫХ ВХОДОВ В СИСТЕМУ, ФАЙЛОВ,

ПРОГРАММ И КОМАНД su Имеется много способов получения несанкциониованных привилегий в системе UNIX. Самый простой способ - иметь корневой (суперпользова- тельский) интерпретатор shell. Это такой shell, который запускается как особо привилегированный процесс, имеющий возможность читать, уда- лять или модифицировать ЛЮБОЙ файл в системе независимо от того, ка- кие права доступа установлены для этого файла его владельцем. Корне- вой shell можно заполучить, узнав корневой пароль у неосторожного администратора или при помощи других средств, рассматриваемых ниже. Несанкционированный пользователь, имея доступ к правам супер- пользователя, может подготовить "потайные двери", обеспечивающие дальнейший запрещенный доступ. Они позволяют нарушителю запускать shell с корневыми привилегиями. Более подробно мы рассмотрим их позд- нее. Потайные двери могут выступать в различном обличье. Они могут быть исполняемыми модулями, латками в системных утилитах или латками в системных файлах. Администратор должен вести постоянное наблюдение за изменениями в системе и уметь противодействовать всяческим вмеша- тельствам. Ниже мы рассмотрим некоторые инструментальные средства и приемы, помогающие вам обнаруживать такое проникновение.

    ВХОДЫ В СИСТЕМУ

Начнем с несанкционированных входов в систему. Это может прои- зойти многими способами. Бывает, что нарушитель добавляет свое собс- твенное регистрационное имя в парольный файл и помещает туда свой па- роль. Если администратор не знаком с парольным файлом или давно туда не заглядывал, то такую несанкционированную запись можно проглядеть. Другой метод несанкционированного входа в систему заключается в том, что кто-то может завладеть всеми паролями, вставляя в программу login "латку" с текстом, направлящим все введенные пользователями па- роли в потайной файл. Ниже мы рассмотрим некоторые типы таких "ла- ток". Конечно, такая изысканная работа зачастую совсем не обязательна. Как известно, люди оставляют свои пароли написанными на листках бума- ги в незапертых ящиках стола. Для некоторых верхом секретности явля- ется применение комбинации первого и последнего имени в качестве па- роля. Однако если уж нарушитель знает много паролей, он может применять каждый раз различные регистрационные имена, чтобы не попа- даться на опасном имени. Пробить систему защиты UNIX можно с помощью "исполняемых регист- рационных имен". Это имена, которые запускают программу, а не просто предоставляют вам shell, что является обычным способом начала сеанса работы пользователя в системе. Это может выглядеть примерно так: date::100:50:Print the date:/bin:/bin/date who::101:50:Print all logged on users:/bin:/bin/who Это может запустить любой, кто имеет доступ к терминалу или мо- демному порту. Иногда это правильные имена, например date, who или sync. Хотя для администратора может быть удобным наличие программ, запускаемых при входе в систему, они часто становятся лазейками, че- рез которые кто-нибудь может проникнуть в систему и обнаружить много информации о системе. Самые крупные лазейки появляются тогда, когда эти регистрацион- ные имена выполняют командные файлы интерпретатора shell. Как только нарушитель получает привилегии суперпользователя (даже если они лишь временные), он может поместить в парольный файл такую запись, которая в момент входа в систему запускает командный файл интерпретатора shell (или может изменить имеющуюся запись с командным файлом). Сами эти командные файлы можно в любой момент изменить так, чтобы они ра- ботали по заданию несанкционированного пользователя. Например, в па- рольном файле может быть такая запись: break::102:50::/:/usr/bin/break Такая запись позволила бы кому угодно набрать имя "break" в ответ на регистрационную подсказку, в результате чего выполнился бы файл /usr/ bin/break. Когда break отработает, снова поступает регистрационная подсказка, и в системе появляется новая лазейка. Почему? Потому что командный файл break может содержать команды для редактора, которые отредактировали бы парольный файл и добавили несанкционированные за- писи. Это становится возможным по той причине, что процесс getty (пе- чатающий регистрационную подсказку) запускается процессом init, а владельцем файла init является суперпользователь. Такая привилегия передается командному файлу, так как он запущен в момент регистрации в системе, а программам, запускаемым при входе в систему, обычно тре- буется суперпользовательский доступ для выполнения необходимых иници- ализаций. В данном случае, однако, он позволяет редактору читать файл /etc/passwd и писать в него. Таким образом, как только нарушитель ОДИН РАЗ получает доступ на запись в /etc/passwd (аналогично "дивер- сионным программам"), он может установить постоянный доступ, часто даже через несколько точек входа. И еще. В старых версиях UNIX попадаются некоторые ошибки, пре- доставляющие суперпользовательские возможности. Например, если в пользовательской записи парольного файла не указан номер пользова- тельского идентификатора, то по умолчанию он считается нулевым, т.е. суперпользовательским. В эту лазейку очень легко проникнуть. Пример такой записи: rt::::The Super User:/:/bin/sh Вот некоторые другие проблемы, за которыми должен следить адми- нистратор. Если первая строка парольного файла пустая, то пользова- тель может зарегистрироваться как корневой без пароля. Проверьте так- же запись "bin" в парольном файле, которая обычно запускает системные программы. Если запись bin не содержит пароля, как в приведенном выше примере, кто-то может войти в систему в качестве bin и отредактиро- вать файл crontab, чтобы применить к парольному файлу команду chmod (change permission mode, изменение прав доступа) и обеспечить себе доступ к нему. Пользователь bin может также отредактировать файл /etc /rc, чтобы сменить парольный файл. Файл rc используется для конфигу- рирования системы в момент ее старта путем автоматического запуска ряда программ. Все, что нужно для успешного вторжения,- подождать, когда администратор перегрузит систему (поскольку именно в этот мо- мент файл запускается). После перезагрузки нарушитель может войти в систему как обычный пользователь, отредактировать парольный файл, за- писать его, а потом в любой момент входить в систему в качестве су- перпользователя. Это всего лишь несколько способов, которыми можно добиться несанкционированного входа в систему. К сожалению, каждый день выдумывают новые способы.

    ФАЙЛЫ И ПРОГРАММЫ

Еще одна сфера злоупотреблений связана с несанкционированным проникновением в файлы и программы. Самый трудный этап для того, кто хочет взломать защиту системы UNIX,- стать суперпользователем первый раз, но как только эта цель достигнута какими-либо средствами, файлы- интервенты можно поместить в любом месте системы. Вторжение может включать в себя размещение "потайных дверей", латание команды login с целью овладения паролями, чтение и изменение системных учетных файлов и т.д. Ниже мы рассмотрим примеры этих и других методов. Основными файлами, в которые вторгается корневой нарушитель, яв- ляются /etc/passwd, /etc/*rc*, /usr/lib/crontab, /usr/lib/uucp/L.sys. Для обнаружения трещин в вашей административной броне можно поискать файлы, для которых взведен бит разрешения установки пользовательского идентификатора (что указывается буквой "s" в правах доступа, отобра- жаемых командой "ls -l"), и файлы, владельцем которых является супер- пользователь. Назначение бита установки пользовательского идентифика- тора - разрешить программе иметь временный доступ к более привилегированному состоянию (например, суперпользовательскому), чем она имеет в момент своего запуска. На самом деле это очень полезное свойство системы UNIX, так как оно позволяет управлять доступом ко многим таким особенностям, к которым вы бы не хотели предоставить не- посредственный доступ для других пользователей. К сожалению, в эти программы может кто-нибудь проникнуть, чтобы использовать их времен- ный корневой статус для вредительства, которое мы уже описывали. Та- ких файлов имеется конечное число, и все они могут быть проверены. Рассматриваемый далее командный файл chkset автоматизирует для вас процесс проверки. Тем не менее, знание того, какие файлы МОГЛИ быть подвергнуты вмешательству, еще ничего не говорит о том, в какие файлы ДЕЙСТВИТЕЛЬНО произошло вторжение и как. Тяжелее всего обнаружить за- латанные системные файлы. Некоторыми из часто латаемых файлов являют- ся login, su, passwd, ps, crypt и mv. Бывает, что изощренный нарушитель скрывает даты модификации фай- ла, чтобы никто не смог его обнаружить по этому признаку. Единствен- ный способ зафиксировать такое вмешательство - иметь КОНТРОЛЬНУЮ СУМ- МУ, т.е. запись с суммой (количеством байтов) всех важных файлов и хранить ее в отдельном месте или в закодированном виде. Путем перио- дической сверки старых сумм с новыми, можно обнаружить измененные файлы. Еще одна вещь, за которой должен следить администратор, это "скрытые файлы". Скрытые файлы являются частью системы и имеют опре- деленный смысл: они предназначены для того, чтобы не загромождать распечатки каталогов. Для того чтобы скрыть файл, нужно сделать пер- вым символом имени файла точку (.). При использовании команды ls вы должны указать опцию -a, если вы хотите увидеть файлы, начинающиеся с точки. Обнаружение запрещенных файлов может быть затруднено, если файл зарыт на три-четыре уровня каталога вниз и назван незаметным именем. Решение заключается в том, чтобы всегда применять опцию -a команды ls, если вы сталкиваетесь с проблемами. Некоторые команды по умолчанию печатают файлы, начинающиеся с точки. Ncheck(1M) печатает все файлы, имеющие взведенный бит разрешения установки пользователь- ского идентификатора. Если файл назван странным образом, его сразу же видно. Одним из моих любимых является файл "...". Он выглядит нес- колько странно, но это правильное имя файла. Вы даже можете завести имя файла, образованное 14 точками - такова максимальная длина имени файла. КОМАНДЫ su Последний вопрос, за которым нужно следить,- запрещенные команды su. Su - это такое средство, которое позволяет вам ПОДСТАВИТЬ другой пользовательский идентификатор вместо вашего собственного. Если кто-то знает корневой пароль, он может войти в систему с любого тер- минала и применить команду su с корневым паролем. Однако, это, веро- ятно, тот случай, когда нарушители потратят больше всего времени, пы- таясь чего-либо добиться. Дело в том, что все транзакции su записываются в протокольный файл под названием sulog. Правда, к сожа- лению, если уж нарушитель стал суперпользователем, то ему ничто не мешает модифицировать протокольный файл с целью удаления компромети- рующих записей. К тому же если редактор vi вызван без имени файла, то никто не может увидеть, какой файл редактируется в то время, когда в системе происходит вредительство. Но бдительный системный администратор может бороться с этим при помощи команды ps. Она печатает строку о команде su точно так же, как она делает это для всех остальных процессов, поэтому можно сразу же заметить, что кто-то превратился в суперпользователя командой su. На- рушителя выдает то, что родительский процесс имеет регистрационное имя, а владельцем su является суперпользователь. Наконец, все равно же нужен корневой пароль. А если кто-то уже знает корневой пароль, то зачем ему связываться с командой su? Применять su было бы резонно только в том случае, если бы залатать команду su так, чтобы она не записывала транзакцию в протокол и изменяла строку, которую печатает ps. Мы еще не знаем, чтобы кто-нибудь добился такого эффекта.

    СОХРАНЕНИЕ КОНФИДЕНЦИАЛЬНОСТИ ДАННЫХ

Даже если допустить, что секретность обеспечена, бывают случаи, когда администратору нужно защитить важные файлы от любопытных глаз. В системе UNIX это можно сделать с помощью специальных атрибутов за- щиты файла, специальных групповых прав доступа, шифровки или даже размещения этих данных на диске, который монтируется только в случае необходимости. Однако, такие данные не должны оставаться физически присутствующими в системе, если они не монтированы, потому что нару- шитель может их смонтировать и получить к ним доступ. Командный файл mntlook, рассмотренный ранее, умеет просматривать все устройства и находить такие доступные, но немонтированные файловые системы. Необходимо соблюдать такое правило: "Если вы не хотите, чтобы кто-нибудь видел этот файл, не держите его на виду". И не думайте, что вы так хорошо его спрятали, что никто не сможет его найти. Если люди имеют суперпользовательский доступ в вашу систему, они могут за считанные минуты получить распечатку каждого файла этой системы. За- тем, когда вы не видите, они могут при помощи uucp передать интерес- ный файл в другую систему для последующего изучения, скопировать его на гибкий диск или даже отпечатать. Помните, что если в вашу систему проник несанкционированный пользователь, НИКАКОЙ БЕЗОПАСНОСТИ БОЛЬШЕ НЕТ!

    КОНТРОЛЬ ЗА ИСПОЛЬЗОВАНИЕМ МОДЕМА

Модемы являются одной из крупных пробоин в защите системы. Если только у вас нет специальной аппаратуры для предварительной фильтра- ции обращений в систему UNIX, то она всегда уязвима посредством мо- демных портов. Большие вычислительные системы могут иметь произвольное число модемов, как принимающих, так и передающих. Вам может показаться, что поскольку команда login имеет дополнительный пароль для линий с набо- ром номера, то все секретно, но это не так. Имеются программы, кото- рые могут пробовать много комбинаций вероятных регистрационных имен и паролей, и в случае подходящей комбинации команда login может впус- тить нарушителя в систему! Обращение вовне, в другие системы через модемы - отдельная исто- рия. Обычно тот, кто правильно зарегистрировался в системе, хочет обращаться к другим системам. Но что, если на вашей стороне имеются улавливающие регистрационные имена типа class, education, test и т.д.? Кто-то может войти в систему под видом одного из таких пользо- вателей и использовать модем безо всякого риска быть схваченным за руку. Единственный способ поймать таких нарушителей - по номеру тер- минальной линии, если у вас имеются специально выделенные линии. Что произошло бы, если бы тот, кто вошел в вашу систему через модем при помощи одного из перечисленных регистрационных имен, обра- тился бы потом вовне, к какой-нибудь "дальней земле"? Тогда не было бы никакой возможности уследить за обратным вызовом определенного пользователя.

    ПРЕДОТВРАЩЕНИЕ ЗАПРЕЩЕННЫХ ПЕРЕСЫЛОК ФАЙЛОВ

Запрещенные пересылки файлов имеют отношение почти исключительно к средствам uucp. В системе Berkeley (BSD 4.1 и старше) сетевые ко- манды также имеют аналогичные проблемы. Вот пример: если кто-то за- пускает в системе Berkeley командный файл для "взлома двери", то ко- манда удаленной регистрации в системе (rlogin) регистрирует нарушителя на другой машине в качестве суперпользователя и никогда не спрашивает корневой пароль. Разве это не очевидная пробоина в систе- ме? Несанкционированный пользователь может также применить удаленное копирование (rcp), чтобы скопировать программу "взлома двери" во все системы. Самое главное следить за протокольными файлами. Но опять же, что если нарушитель удаляет из протокольных файлов все записи, связанные с заданием вопросов? У вас нет способа узнать о том, что это произош- ло. Еще нужно следить за таким поведением нарушителей, когда они де- лают вызов и выдают себя за корректную удаленную систему. Они могут добиться этого, изменив узловое имя своей системы таким образом, что- бы оно соответствовало одному их ваших разрешенных "корреспондентов". Изощренного нарушителя очень трудно поймать, но мы предлагаем некото- рые идеи, которые должны вам в этом помочь.

    СВЕДЕНИЕ К МИНИМУМУ ВОЗМОЖНОСТЕЙ ВЗЛОМА

Это то, чем администраторы часто пренебрегают. Совет номер один - НИКОГДА не оставлять без присмотра терминал, зарегистрированный как суперпользовательский. Бросить без присмотра терминал с корневым дос- тупом - все равно, что оставить тысячу ключей от сейфа компании на вашем столе. Все несанкционированные пользователи могут воспользо- ваться этим, подготовив командный файл "взлома двери", ожидающий та- кого момента. Как только они получают в свои руки ваш терминал, всего лишь одна команда предоставляет им безграничные суперпользовательские возможности. С этого момента система перестает быть защищенной. Системные администраторы должны проверять свои системы и смот- реть на них с точки зрения нарушителя. Есть ли хоть когда-нибудь мо- мент, в который система уязвима? Что может произойти среди ночи, ког- да работают программы резервного копирования? Может ли кто-нибудь завладеть консольным терминалом? Не сможет ли навредить тот, кто при- ходит к вам помогать? Если вы на секундочку вышли, не сможет ли кто-то применить команду chmod, а потом заполнить экран чем-нибудь другим, чтобы вы не узнали, что это было сделано? Вот те опасности, о которых вам нужно помнить.

    ТИПИЧНЫЕ ПРОБЛЕМЫ БЕЗОПАСНОСТИ

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

    ПОТАЙНЫЕ ДВЕРИ

Мы уже отмечали, что люди, которые могут получить суперпользова- тельский доступ в систему хотя бы на короткое время, могут написать программы, предоставляющие им постоянный суперпользовательский дос- туп. Напомним, что тот, кто хочет прорвать защиту системы UNIX, пер- вым делом пытается найти способ стать суперпользователем. Как мы уже обсуждали, нарушение физической защиты или плохо оберегаемый корневой пароль могут дать нарушителю возможность запустить процесс как супер- пользовательский, что предоставляет доступ к файлам (например, стан- дартным исполняемым модулям системы UNIX), которые не может изменить обычный пользователь. В результате нарушитель получает для себя "по- тайную дверь". Ключевым вопросом является способ хранения в системе UNIX указа- ний о владельце и привилегиях, связанных с файлом. Помимо хорошо из- вестных прав доступа для владельца, группы и прочих пользователей (эти права устанавливаются командой chmod), имеется два более старших бита, называемых setuid (установка пользовательского идентификатора) и setgid (установка группового идентификатора). Как правило, процесс, запущенный данным пользователем, имеет только те привилегии на доступ, которые принадлежат этому пользовате- лю. Однако, многие системные команды должны иметь доступ к таким фай- лам, к которым мы бы не хотели разрешать доступ пользователя, за иск- лючением очень ограниченного набора ситуаций. Ярким примером является команда passwd, позволяющая пользователю сменить свой пароль. Очевид- но, что этой команде необходим доступ на запись в файл /etc/passwd, а такой доступ имеет обычно только суперпользователь. Проблему решает исполняемый файл команды passwd, в котором бит setuid установлен на владельца файла, а владельцем файлов, соответс- твующих обычным системным командам, является суперпользователь (поль- зовательский идентификатор 0). Это означает, что ВО ВРЕМЯ РАБОТЫ ПРО- ЦЕССА, соответствующего данной команде, пользователь имеет корневые привилегии! Когда команда завершается, прекращается и корневой доступ ... если только в данную команду не было какого-то вмешательства или если нарушитель не установил особую программу setuid. В этих случаях остается только войти в потайную дверь. Потайная дверь - это чаще всего файл, владельцем которого явля- ется суперпользователь, но который подвергнут вмешательству несанкци- онированного пользователя, завладевшего каким-то образом правом дос- тупа на запись в этот файл, обычно путем временного суперпользовательского доступа. Важно понимать, что потайная дверь - это просто еще один процесс, порожденный из обычного пользовательско- го интерпретатора shell, но с одной существенной особенностью: у него другой номер пользовательского идентификатора - как правило, 0, т.е. идентификатор суперпользователя. Поскольку пользовательский идентифи- катор хранится в самом процессе, он может быть подвергнут вмешатель- ству. Фактическое проникновение в систему с обретением возможностей суперпользователя происходит тогда, когда работает "дверная" програм- ма. Здесь используется волшебство бита setuid. Когда этот бит взве- ден, программа устанавливает (или изменяет) пользовательский иденти- фикатор процесса на пользовательский идентификатор владельца данного файла (который оказывается суперпользовательским). Пока этот пользо- вательский идентификатор временно является суперпользовательским, программа превращается в shell-интерпретатор (обычно путем выполнения системного вызова exec). Такой shell находится по другую сторону две- ри, в царстве суперпользователя, со всеми принадлежащими ему привиле- гиями. Как мы уже отмечали, обсуждая команды su, более изощренные нару- шители могут различными способами маскировать свое прониконвение в систему. Один из способ маскировки - иметь "дверную" программу, кото- рая ничего не делает, если только она не вызвана с какой-нибудь неза- метной опцией, например -z. Скорее всего, программа потайной двери не выдает сколько-нибудь полезного синтаксического сообщения, если ее вызвать без правильной опции. Еще одна хитрость заключается в том, что программа потайной две- ри может изменить свою командную строку (которую можно отобразить при помощи команды "ps -ef", выдающей полное состояние процесса) на какую -нибудь безобидную, запускаемую обычно суперпользователем (например, getty). Опытный нарушитель вряд ли оставит исходный текст программы по- тайной двери в системе, поэтому администратор вынужден разглядывать только исполняемый модуль. Для реассемблирования объектного кода мож- но применить отладчик (adb), но если только вы не имеете безумно близкого знакомства с внутренностями системы UNIX, вам будет весьма трудно представить себе, что происходит. Изощренные программы потай- ной двери избегают также присутствия легко узнаваемых строк в испол- няемых модулях. Вы можете, однако, применить команду strings (если она есть в вашей системе) для поиска символьных строк, которые там могли бы быть.

    ПРОТОКОЛЬНЫЕ ФАЙЛЫ

Одна из простейших ловушек для того, кто пытается добыть права суперпользователя - создавать запись, помещаемую в протокольный файл. При этом администраторам нужно следить за протокольными файлами, не появляются ли там записи, которые могут быть признаком злодейства. Ниже мы покажем вам инструментальное средство, которое автоматически следит за одним из таких протокольных файлов - файлом sulog, содержа- щим транзакции "замененного пользователя". Другой протокольный файл, часто нуждающийся в проверке, это протокол программы uucp, потому что эта программа может быть использована для несанкционированных пересы- лок файлов. Многие нарушители пытаются проверить протокольные файлы и удалить компрометирующие записи, сгенерированные при их вмешательст- ве. В арсенале администратора есть средства борьбы с этим. Они не на 100 процентов эффективны, но отлавливают некоторых нарушителей и, ус- ложняя им жизнь, могут отбить охоту вторгаться в систему. В дополнение к обычным протокольным файлам, поддерживаемым сис- темой UNIX, некоторые администраторы заводят свои собственные прото- кольные файлы, а затем подправляют ключевые команды так, чтобы они помещали данные в эти новые регистрационные файлы в процессе своей работы. Это может помочь обезвредить неосторожных лазутчиков. Один знакомый администратор сделал протокольный файл для команды cu и наз- вал его /tmp/.../.culog. Довольно умный тайный фокус, но в /usr/lib/crontab у него была запись для периодической печати этого файла. Это его выдавало: нужно было маскироваться получше. Заметим также, что ваши "скрытые" имена протокольных файлов могут быть извле- чены путем просмотра исполняемого образа команды с помощью утилиты strings. Если у вас есть пользователь, от которого вы ожидаете чего-ни- будь запрещенного, вы должны уметь установить специальную систему ре- гистрации, которая запускала бы более совершенный механизм, когда та- кой пользователь работает в системе. Программу watch из главы 6 можно модифицировать так, чтобы она вызывала специальную протоколирующую программу, когда в систему входит пользователь из известного списка. Протоколирующая программа могла бы повторять команды ps (process status, состояние процесса) и/или делать "моментальные снимки" обыч- ных регистрационных файлов (особенно учетных файлов) и направлять ре- зультаты в припрятанный протокольный файл. Идея состоит в том, что опасные процессы можно было бы обнаружить до того, как нарушитель по- лучает возможность войти в протокольные файлы и изменить их. (Видимо, вам нужно избегать применения команды at в такой программе, а перио- дически пользоваться вместо нее командой sleep. В противном случае, нарушитель может распознать ваши мероприятия по записи файла crontab.) Как только вы имеете результат о сеансе работы опасного пользователя, вы можете запустить grep для поиска интересующих вас имен или написать инструментальное средство, которое выполняет для вас такой поиск.

    УЧЕТНЫЕ ФАЙЛЫ

Вероятно, наиболее важным среди протокольных файлов является учетный файл. В учетном файле имеется запись о каждом и всяком про- цессе, запускаемом в системе. Точную структуру можно посмотреть в файле /usr/include/sys/acct.h. В одном из полей этой структуры запи- саны процессы, имеющие суперпользовательские возможности. Когда кто-либо входит в систему через корневую дверь, для shell- интерпретатора, который он запускает, и для всех процессов, которые он порождает, владельцем является корень (суперпользователь). В учет- ном файле отражен номер терминала, с которого запущен процесс, поэто- му вы можете увидеть корневые процессы, запущенные с таких термина- лов, на которых пользователям не разрешен суперпользовательский доступ. Если у вас имеются обычные линии с набором номера, то все такие записи могут представлять не одного и того же пользователя. Другие входы в систему могут иметь одинаковый номер терминала, но разные пользовательские идентификаторы. Однако, вы можете знать, кто обычно имеет доступ к некоторым выделенным линиям. Учетные файлы могут хорошо разоблачить процессы, имеющие не та- кой пользовательский идентификатор, как у лица, запустившего эти про- цессы. Поищите процессы, владельцем которых был известный пользова- тель, но которые имеют суперпользовательские возможности. Среди них могут быть корректные записи, например lpr, так как отображаются все системные программы, запущенные со взведенным битом setuid. Записи, которые мы ищем, относятся к shell-интерпретаторам с установленным учетным флагом суперпользователя. Это выдает тот факт, что была вы- полнена программа потайной двери. Изучите подключаемый файл acct.h, чтобы увидеть все определения. Используя бит ASU для проверки поля, мы можем изолировать флаговую область, отражающую привилегию супер- пользователя. Самый лучший способ рассмотреть эту структуру - напи- сать программу на языке Си, печатающую все элементы структуры. В сле- дующей распечатке показаны некоторые важные учетные поля:
| | cmd f uid tty btime | | more 0 russ 0 Sat Jul 5 01:25:59 1986 | ls 0 russ 0 Sat Jul 5 01:31:12 1986 | ps 0 russ 0 Sat Jul 5 01:31:59 1986 | id 0 russ 0 Sat Jul 5 01:34:00 1986 | pwd 0 russ 0 Sat Jul 5 01:34:12 1986 | sh 1% russ 0 Sat Jul 5 01:33:51 1986 | \__ корневой shell с эффективным пользовательским идентификатором | | sync 0 russ 0 Sat Jul 5 01:34:21 1986 | df 0 russ 0 Sat Jul 5 01:34:27 1986 | id 0 root 0 Sat Jul 5 01:34:37 1986 | sh 2# root 0 Sat Jul 5 01:34:33 1986 | \__ корневой shell с реальным пользовательским идентификатором, | 2# обозначает бит суперпользователя, | владелец изменен на root | Отметим, что shell-интерпретаторы с эффективным пользовательским идентификатором маскируют бит во флаге суперпользователя, но владель- цем их процессов является обычный пользователь. Не известно, все ли системы применяют значение 1 в качестве флага shell-интерпретатора с эффективным пользовательским идентификатором. Похоже, что Berkeley поступает именно так, а System V нет. КОМАНДА su Как мы уже отмечали, UNIX предоставляет пользователям обычный способ стать суперпользователем - это команда su. Как видно из преды- дущего изложения, суть команды su заключается в системном вызове exec. Тот, кто применяет команду su, должен знать корневой пароль, транзакция протоколируется в файле /usr/adm/sulog, а команда ps огла- шает тот факт, что активен корневой shell. Этот прием не самый хитро- умный.

    ДОСТУП К ТЕРМИНАЛУ СУПЕРПОЛЬЗОВАТЕЛЯ

Помните о том, что НИКОГДА нельзя оставлять без присмотра терми- нал, на котором вы работаете в качестве суперпользователя? Пока вы отсутствуете, кто-то может воспользоваться этим терминалом, чтобы вы- полнить команду chmod с целью установки бита пользовательского иден- тификатора. Хорошо подготовленный нарушитель уже имеет откомпилиро- ванную программу потайной двери, владельцем которой является суперпользователь, и ждет, когда будут изменены права доступа. Не ос- таются ли консольные терминалы доступными в качестве суперпользова- тельских ночью, во время действий по резервному копированию? Если ос- таются, то наутро администратор может оказаться в незавидном положении! БОЛЕЕ ПОДРОБНО О ПОЛЬЗОВАТЕЛЕ bin Мы уже упоминали о "лазейке" в некоторых системах, позволяющих пользователю "bin" без труда завладеть суперпользовательским досту- пом. Проблема "bin" имеет еще ряд аспектов. Если несанкционированные пользователи могут войти в систему через bin (являющийся владельцем большинства дистрибутивных исполняемых модулей), то они почти навер- няка смогут получить права суперпользователя. Прежде всего, в некото- рых версиях владельцем всех исполняемых модулей, размещенных в ката- логах /bin и /usr/bin, является bin. Это значит, что нарушители могут перезаписать или подправить исполняемые модули своими собственными вариантами, выполняющими некоторые особые действия, например "chmod 4755 door", а затем восстановить на место исходную версию исполняемо- го модуля. Еще один способ простого превращения bin в суперпользователя заключается в модификации /etc/rc - файла запуска команд ("run command"). Он запускается каждый раз, когда машина загружается в мно- гопользовательском режиме. Записывая в него "chmod 777 /etc/passwd", нарушитель может превратить парольный файл в обычный пользовательский после того, как машина загрузится. Последний способ - использовать файл /usr/lib/crontab. Этот путь изменен в последней версии System V. Теперь полное имя этого файла /usr/spool/cron/crontabs/xxx, где xxx - файл crontab для каждого пользователя. В старом варианте владельцем файла /usr/lib/crontab иногда является bin. Кто-нибудь может отредактировать этот файл и по- местить в него такие, например, команды: * * * * * chmod 777 /etc/passwd * * * * * chmod 4755 /tmp/door * * * * * /bin/su root -c "chmod 777 /etc/passwd" Все это срабатывает по той причине, что cron выполняется процес- сом init. Поскольку init - один из первых запущенных процессов, его владельцем является суперпользователь. Поэтому любая команда, которую выполняет cron, имеет корневые привилегии. Звездочки означают, что cron должен выполнить указанные команды в очередной возможный момент времени. Cron запускает процесс, изменяющий права доступа к указанно- му файлу. Нарушители могут поменять парольный файл (что несколько бо- лее опасно) или просто активизировать программу потайной двери. Если две первые команды не сработают, то владелец будет изменен на супер- пользователя методом грубой силы - выполнением команды su, а затем передачей команды chmod shell-интерпретатору, запущенному командой su. Вы должны почаще проверять файлы crontab и /etc/rc!

    ВОЗМОЖНОСТЬ ЗАПИСИ В СПЕЦИАЛЬНЫЕ ФАЙЛЫ

Это редко применяемый метод. Он предполагает, что обычный поль- зователь имеет право на запись в исполняемый модуль или в специальный файл. Если кто-то может писать в исполняемый модуль, скажем ls, то он может поместить туда некоторый код, который устанавливает потайную дверь, а затем заменяет себя настоящей утилитой. Это работает еще лучше, когда эту команду запускает пользователь с корневыми возмож- ностями, так как тогда команда выполняется как привилегированный про- цесс. Такой способ упрятывания кода в программу с возможностью запус- ка ее не вызывающим подозрения пользователем называется "троянским конем". Он выглядит безвредным, но то, что спрятано у него внутри, крепко бьет по защите системы. Как мы уже отмечали, периодическая проверка контрольных сумм для стандартных исполняемых модулей - одно из противоядий от "троянских коней". Если нарушители овладевают возможностью чтения или записи файлов устройств, они могут прочитать в этих файлах информацию о суперблоке и файловой системе и получить доступ к любому файлу. Им может понадо- биться пройти по файловой системе в поисках нужного файла, подправить его и записать обратно в файл устройства. Люди, которые этим занима- ются и совершают при этом ошибки, могут добиться краха всей системы. Если файл устройства находится в памяти, пользователи могут просматривать информацию о процессах или ядре непосредственно в памя- ти. Может быть использован старый прием отслеживания списков clist на предмет поступления пароля пользователя, входящего в систему. Это требует больших знаний о том, где данная информация размещается в па- мяти и как к ней добраться, поэтому этим методом пользуются, вероят- но, только опытные нарушители.

    ПЕРЕЗАГРУЗКА СИСТЕМЫ

В некоторых системах бывает, что после перезагрузки машины на консоли работает суперпользовательский shell. Такое может произойти в однопользовательском режиме или на микро-ЭВМ, служащей консолью для большой машины. Очевидное решение - ограничить физический доступ к консоли. ИСПОЛЬЗОВАНИЕ ПРЕИМУЩЕСТВ ПЕРЕМЕННОЙ $PATH Кое-кто может воспользоваться ситуацией, когда пользователь име- ет каталог $HOME/bin впереди системных каталогов /bin и /usr/bin в своей переменной PATH. Помещая подправленную программу в регистраци- онный каталог безвредного пользователя, нарушитель заставляет эту программу выполняться вместо настоящей, поскольку система исполняет первый файл, совпадающий с именем команды, который она обнаруживает в ходе просмотра определенного пользователем маршрутного списка. Приме- ром могла бы служить команда ls. Когда команда выполняется, она запускается с теми же правами доступа, что и запустивший ее пользователь. Конечная цель нарушителей - добиться прав суперпользователя, поскольку тогда они смогли бы вы- полнить любую команду. Диверсионная программа может подготовить по- тайную дверь для овладения правами суперпользователя или даже нес- колько таких дверей. Она может удалить себя, чтобы при очередном вызове данной команды выполнялась настоящая команда. Настоятельная необходимость для администратора - проверять наличие в рабочем прост- ранстве опасных файлов (в частности, исполняемых модулей).

    ФАЛЬШИВЫЕ ВЕРСИИ НА ЛЕНТАХ

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

    ЗАШИФРОВАННАЯ БАЗА ДАННЫХ

Хотя мы не можем дать гарантию, что кто-нибудь смекалистый не опишет, как расколоть парольную защиту, кодирование пароля в системе UNIX по алгоритму DES считается довольно секретным. (Рассмотрение ме- ханизма DES выходит за пределы данной книги.) К этой защите добавля- ется одна вещь - два символа, генерируемые случайным образом, называ- емые "солью" (salt) и хранимые в файле /etc/passwd для каждой записи. "Соль" используется для определения того, какой из 4096 вариантов ал- горитма DES применяется для кодирования заданного пароля. Нарушитель мог бы захватить пароль, использовать его "соль" и зашифровать список известных паролей. Если бы результат совпадал с пробным паролем, взлом был бы осуществлен. Нарушителю пришлось бы иметь доступ к некоторой довольно чувс- твительной методике, чтобы добиться своего. Нелишняя мера, которую может предпринять администратор,- следить за чрезмерным использовани- ем команды crypt (если она есть в вашей системе). Эта команда специ- ально сконструирована так, чтобы занимать много процессорного времени - не настолько много, чтобы причинять неудобства законным пользовате- лям, но достаточно много, чтобы выявить попытки автоматизированного взлома.

    ЗАПАДНЯ

Западня работает только на специально выделенных линиях. Когда UNIX ожидает, что кто-то будет входить в систему, она печатает ре- гистрационную строку из файла /etc/gettydefs. Пользователь вводит свое регистрационное имя и пароль и попадает в систему. Программа западни извлекает из этого выгоду. Она имитирует пове- дение экрана во время регистрации. Когда пользователь вводит свое имя и пароль, такая программа печатает сообщение "login incorrect" ("не- верная регистрация"), а затем запускает настоящую регистрационную программу. Пользователь думает, что он сделал ошибку и повторяет по- пытку входа в систему, на этот раз успешно. Регистрационное имя и па- роль можно куда-нибудь отложить для последующего изучения. Самая луч- шая защита администратора от этого - обучение пользователей. Видимо, с помощью регулярной почты или бюллетеней нужно напоминать им, что если им кажется, что они набрали пароль правильно, но получили при этом сообщение "incorrect", то они должны немедленно сменить свой па- роль и сообщить об этом происшествии администратору. Бывают случаи, когда пользователь действительно ошибается при вводе пароля, но к большому количеству таких происшествий нужно отнеститсь со всей серь- езностью.

    КОМАНДА МОНТИРОВАНИЯ

Команда монтирования была создана для того, чтобы позволить при- соединять к системе несколько дисковых устройств. Раньше, в эпоху ми- никомпьютеров единственным видом дисковых устройств были большие на- копители, в которые вставлялись большие дисковые пакеты. Они обычно находились в машинном зале, и только администратор монтировал их. Теперь многие системы имеют гибкие диски. Они гораздо персональ- нее и намного меньше, чем большие дисковые пакеты. Это уменьшение размера ощутимо воздействует на важность самого объекта. Похоже, что теперь каждый пользователь может иметь дело со своими дисками, и ад- министратору больше нет необходимости заниматься этим. Такой сценарий работы приводит к следующему. Обычное действие, предпринимаемое в небольших системах для того, чтобы дать пользователям возможность работать с их собственными гиб- кими дисками,- это установка бита пользовательского идентификатора для файла команды /etc/mount. Таким образом, когда запускается коман- да монтирования, ее пользовательский идентификатор становится корне- вым, и она может смонтировать гибкий диск. Команда демонтирования должна быть подготовлена аналогично. Кто-то может извлечь выгоду из того, что команда mount может по- лучить во время своей работы права суперпользователя. Обычно в не- больших системах одна машина открыта для экспериментов несанкциониро- ванного пользователя. Он может стать суперпользователем в вашей системе, подготовить программу потайной двери и поместить ее на гиб- кий диск. Владельцем является суперпользователь, а права доступа 4755. Затем нарушитель может размонтировать гибкий диск и войти в за- щищенную систему. С этого момента нарушитель может просто входить в систему как обычный пользователь, без всяких специальных прав доступа и монтиро- вать гибкий диск, на котором имеется программа потайной двери. Когда гибкий диск монтируется, файловая система гибкого диска встраивается в файловую систему жесткого диска, и две системы сливаются в одну. Это означает, что потайная дверь для овладения правами суперпользова- теля готова для файловой системы на жестком диске. Когда нарушитель запускает программу потайной двери на гибком диске, происходит то же самое, что происходило бы, если бы эта прог- рамма была на жестком диске. Мера предотвращения - контролировать ис- пользование команды mount в вашей системе. Доступ к команде mount должен быть ограничен определенным кругом пользователей, она не долж- на разрешать монтировать файлы с установкой пользовательского иденти- фикатора. АВТОНОМНЫЙ shell (SASH) В системах UNIX с гибкими дисками загрузочный диск обычно загру- жается с гибкого накопителя. Подразумевается, что гибкий диск приме- няется для подготовки жесткого диска и копирования всех файлов систе- мы UNIX с гибкого диска на жесткий. Но за этим кроется нечто большее. На самом деле загружаемый гибкий диск - это компактная, перено- симая версия системы UNIX. Ядро адаптировано к тому, чтобы размещать- ся на гибком диске, а не на жестком. Когда вы его загружаете, вы по- лучаете интерпретатор shell и среду точно так же, как при работе с жесткого диска. Вот почему такой shell называется автономным (SASH, standalone shell). Корневая файловая система на гибком диске даже выглядит точно так же, как файловая система жесткого диска. Фактически вы можете смонтировать загружаемый диск и скопировать утилиты с жесткого диска на гибкий. Нужны две важные команды: mount и umount. Ограничением яв- ляется размер гибкого диска. На него не так много помещается. Сценарий, с помощью которого несанкционированный пользователь может применить SASH для входа в систему с суперпользовательскими привилегиями, выглядит примерно так. Сначала он должен отключить пи- тание или перезагрузить защищенную систему. Затем он должен загрузить SASH и смонтировать корневую файловую систему жесткого диска в точку монтирования своей файловой системы гибкого диска. Команды могут быть такими: # /etc/mount /dev/fp001 /mnt <-- для System V # /etc/mount /dev/hd0a /mnt <-- для XENIX Это дает нарушителям доступ на жесткий диск при помощи обращения /mnt/*. Все, что им нужно для редактирования файла /etc/passwd - это пройти вниз по дереву каталогов. Для этого можно применить такие ко- манды: # /mnt/bin/vi /mnt/etc/passwd # sync Теперь жесткий диск изменен, и его можно вернуть на место. Нару- шитель может остановить автономный вариант UNIX и перезагрузиться с жесткого диска. Он может использовать новое регистрационное имя, соз- данное при помощи SASH. Мы не знаем, насколько часто люди могут предпринимать такие попытки. Небольшие системы более уязвимы, но в них и меньше пользователей (и потенциальных злодеев). Во многих слу- чаях нижней границей "логической защиты" становится "физическая защи- та". Большинство людей, имеющих большие машины или даже мини-ЭВМ, постоянно осознают, что им есть что защищать. А микро-ЭВМ выглядят настолько "дружественными" и простыми, что люди обычно забывают, что информация, которая содержится на микрокомпьютерах, при определенных обстоятельствах может оказаться настолько же желаемой и значительной, как и на больших машинах.

    ПРАВКИ ИСХОДНЫХ ТЕКСТОВ

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

    ЯДРО

Еще одно место, в котором нужно следить за несанкционированными правками - библиотеки ядра. Подправленные объектные модули можно лег- ко поместить в библиотеки незамеченными. Другим библиотекам грозит та же опасность. Дополнительные разумные усовершенствования ядра можно обнаружить в системных вызовах chmod и chown. Когда выполняются эти системные вызовы, они проверяют, имеете ли вы пользовательский иден- тификатор 0. Если нет, то ваш запрос не удовлетворяется. Отменив эту проверку, любой обычный пользователь мог бы изменить владельца файла на суперпользователя, а также изменить режим защиты файла, чтобы взвести бит установки пользовательского идентификатора. Это позволило бы успешно обойти защитный барьер. ПРОГРАММА passwd Программа passwd - охранник ворот системы UNIX. Точно так же, как многие древние города пали из-за того, что враги подкупили охран- ника ворот, так и хорошо защищенная система UNIX может быть превраще- на в широко открытую, если кто-либо подправит эту программу. Посколь- ку пользователи применяют программу passwd для изменения своих паролей, подправленная версия может записывать новый пароль, вводимый пользователем для изменения, в секретный файл, принадлежащий наруши- телю. Это может обесценить каждый вновь создаваемый в системе пароль. Внутри программы passwd пароль является просто символьным массивом, поэтому с этими данными легко управиться. ПРОГРАММА crypt Потенциальной правкой программы шифрования файлов crypt может быть накопление имен файлов и ключей шифра при каждом использовании программы. Таким методом вы можете проследить, кто запускает эту ко- манду, какой файл он использует и какой ключ применяется для того, чтобы получить доступ к этому файлу. Неприятности, связанные с последними двумя случаями, заключаются в том, что кто-нибудь может разрушить меры системной безопасности. Если вы считаете, что в вашей системе есть заманчивые, важные данные, то вам как администратору следует почаще проверять эти программы (по контрольной сумме или сравнением) на предмет повреждения. КОМАНДА su Поскольку команда su предоставляет суперпользовательский доступ для обычных пользователей, имеющих корневой пароль, это еще одна по- тенциальная лазейка в защите системы. Общая схема работы команды su выглядит так:
| | Получить информацию о пользователе: пользовательский идентификатор, | групповой идентификатор, пароль, номер терминала, ... | Если пароль пустой или пользовательский идентификатор равен нулю, | То пройти мимо вопросов о пароле. | | Запрос пароля | Если зашифрованный вариант того, что было только что набрано, | не совпадает с парольной строкой из файла /etc/passwd, | То запротоколировать неудачную попытку применения su, | напечатать сообщение "sorry" (сожаление по поводу неудачи), | выйти. | Пароль прошел: | запротоколировать успешную попытку применения su, | выполнить системные вызовы для того, чтобы ввести в действие | пользовательский и групповой идентификатор, | установить среду, если это требуется, | выдать на консоль сообщение, если это корневой shell, | а вы не за системной консолью, | организовать аргументы для показа su в команде ps, | выполнить интерпретатор shell. | Для подправленной версии потребовались бы лишь небольшие измене- ния в приведенной выше последовательности. Вместо того, чтобы сразу же зашифровывать пароль, su могла бы проверить "секретный" пароль на- рушителя. Если введен такой пароль, то проверку пароля и действия по про- токолированию можно обойти, поэтому запрещенный доступ не отразился бы в протоколе. Несанкционированный пользователь добился бы того, что ДРУГИЕ пароли для команды su записывались бы в "секретный" файл. В результате он бы потихоньку получил все интересующие его пароли для потенциального использования. Для пользователя команда su срабатывала бы успешно, если пароль правильный, а нарушитель получал бы в свое распоряжение пароль. КОМАНДА login Несанкционированный пользователь может повредить команду login при помощи тех же методов, что и для passwd. Тем не менее, админист- ратор может сделать нечто большее, чем просто защитить данную прог- рамму от повреждения. По теории, наилучшей защитой является нападе- ние. Поэтому администратор может внести в команду login свои собственные правки и применять ее как систему оповещения о вмешатель- стве. Каждый раз, когда кто-то входит в систему или пытается войти, можно изменить запись об используемых имени и пароле. Это может сиг- нализировать вам о любых попытках нарушителей угадать пароли методом грубой силы. В силу способа, которым реализована команда login, требуется только одно изменение. Алгоритм проверки как пользовательского паро- ля, так и пароля при наборе номера для модемной связи вызывает одну и ту же подпрограмму. К сожалению, мы не можем привести ее здесь, так как несанкционированные пользователи смогли бы применить ее для сбора паролей в своих корыстных целях. Далее, если вы завели ваш собствен- ный секретный файл для протоколирования попыток входа в систему, то вы должны попытаться убедить нарушителей, что они не смогут прочитать его и останутся со своими заботами. Вы можете сделать следующее, хотя это увеличило бы накладные расходы: зашифровать утилитой crypt пароли в протокольном файле с применением вашего собственного секретного ключа. Тогда даже если кто-то прочитает этот файл, он не сможет вос- пользоваться этой информацией.

    ПРОСТИТЕЛЬНЫЕ ГРЕХИ

Рассматриваемые ниже случаи представляют собой менее, но все же потенциально болезненные проблемы безопасности. Они не привлекают способов овладения суперпользовательскими привилегиями, но являются способами надувательства системы и бегства из нее.

    СИСТЕМНЫЙ РЕЖИМ

Данный прием, пожалуй, редко применяется, если только у вас нет человека, который очень близко знаком с низкоуровневым функционирова- нием того или иного процессора, используемого в вашей машине. Он мо- жет проникнуть в сердце аппаратного оборудования и пристроиться по- верх операционной системы. Тем не менее, администраторы должны осознавать, что такие вещи возможны. Во многих процессорах, например в процессоре Motorola 68000, имеется регистр состояния процессора (Processor Status Register), на- зываемый обычно PSW, хотя у разных процессоров он может называться по -разному. PSW содержит бит, определяющий, работает ли машина в "су- первизорном" или в пользовательском режиме. Этот режим важен для мно- гопользовательской аппаратуры, поскольку все пользовательские прог- раммы работают в пользовательском режиме, что сегментирует и защищает память от "коллизий" между процессами. С другой стороны, ядро работает в супервизорном режиме. Это оз- начает, что защита памяти не действует и центральный процессор может изменять содержимое любой ячейки памяти во всей машине. Ядру необхо- дима такая возможность, поскольку ядро поддерживает механизм своппин- га для перемещения процесса в защищенную память и из нее, когда про- цесс выполняется. Если нарушители безопасности могут получить в прграмму, работающую в системном режиме, то они получают возможность изменять всю память в системе. Последствия могут варьироваться от абсолютного разрушения, нап- ример записывания нулей в каждую ячейку памяти, до способности читать и сортировать данные в памяти, включая пароли и другую информацию с очень ограниченным к ней доступом. Для того чтобы добиться системного режима, нарушителю необходима возможность сгенерировать и установить новое ядро. Используемый метод зависит от того, есть ли у нарушителя исходный текст программ ядра. Приводимые нами подробности относятся к процессору 68000, но могут быть аналогичными для других процессоров.

    СИСТЕМНЫЙ ВЫЗОВ

Первый метод - создать "пользовательский" системный вызов. Сис- темные вызовы находятся в исходных файлах с именами вида os/sys?.c. Это примерно 60 системных вызовов, и каждый из них имеет специфичес- кий номер. Этот номер определяется таблицей системных входов - табли- цей адресов точек входа в системные вызовы. Для добавления нового системного вызова необходимо подготовить его исходный код. Когда ядро перекомпилировано и установлено, можно производить системный вызов из любой программы в системе. Как только такой вызов активизирован, он может перевести машину в системный режим. К счастью, не так уж легко для "обычного" пользователя переком- пилировать и переустановить ядро системы. Этот метод, вероятно, тре- бует "внутренней работы". Помогло бы хранение ваших исходных текстов подальше от системы, но если вам нужно иметь системных програмистов, регулярно модифицирующих эти исходные тексты, то все, что в ваших си- лах - ограничить доступ (и подобрать надежных людей)!

    ПСЕВДОУСТРОЙСТВО

Второй метод может быть использован теми нарушителями, которые не имеют исходных текстов, но имеют все библиотеки, образующие ядро. Здесь подход несколько другой, но результат тот же. В соответствии с той же идеей системного режима, цель заключает- ся в том, чтобы в регистре PSW центрального процессора был установлен привилегированный доступ ("супервизорный" или "системный режим"). Вместо того, чтобы использовать надлежащим образом ядро, этот метод пользуется внешним драйвером, который связывается с ядром. Это выпол- няется путем создания псевдоустройства. Псевдоустройство подобно нас- тоящему устройству, но его имя не ведет ни к какой физической перифе- рии. Доступ к псевдоустройству осуществляется с помощью всех тех же самых примитивов (открыть, закрыть, читать, писать), но это доступ к логической области, а не к физической. Для того чтобы определить псевдоустройство, нужно модифицировать главный файл устройства. В главном файле (который называется /etc/master или /usr/sys/conf/master) имеется таблица всех имен драй- веров устройств, связанных с каждым примитивом. Когда создается псев- доустройство, в таблицу драйверов устройств помещается новая запись. В этой таблице содержатся имена всех подпрограмм, поддерживающих при- митивы. Привилегированного режима можно добиться при помощи открытия псевдоустройства. Системный вызов open передает управление драйверу устройства, т.е. добавленному коду. В момент запуска этого кода маши- на уже находится в системном режиме, поскольку когда выполнялся вызов open, он был "пойман" системой и передан программе обработки, функци- онирующей в системном режиме. После этого драйвер устройства может делать то, что он хочет. НАРУШИТЕЛЬ ВЫДАЕТ СЕБЯ ЗА УДАЛЕННЫЙ УЗЕЛ uucp Если команда login подобна сторожу крепости, то программа uucp подобна заброшенному спасательному туннелю, через который враги могут проникнуть во дворец. С приходом межмашинных коммуникаций возникает целый ряд пробоин в защите системы. При помощи uucp несанкционированные пользователи могут попасть в систему, выдав себя за удаленный узел uucp. Это очень легко сделать. Нарушители могут заглянуть в файл /usr/lib/uucp/L.sys в вашей системе и обнаружить, где находятся удаленные системы, путем поиска входов в систему на других машинах. Затем они могут посмотреть в файле /etc/passwd такие входы в систему, которые запускают программы uucico вместо обычного shell-интерпретатора. Если они обнаружат соответству- ющие пароли, они могут попытаться применить некоторые вероятные паро- ли или использовать один из методов внесения правок, рассмотренных ранее, с целью перехвата паролей. Затем нарушитель может изменить имя узла своей системы на имя узла удаленной системы, чтобы выдать себя не за того, кем он на самом деле является. Он может войти в систему под именем uucp или под спе- циальным регистрационным именем, предназначенным для удаленной маши- ны. Программы uucp передают это узловое имя (которое является под- дельным) в вашу систему. Нарушители могут перекачать почту, файлы и т.д. из вашей системы на свою машину. Если у вас есть что-нибудь в очереди, ожидающей отп- равки на законную удаленную машину, нарушители могут сразу там очу- титься. Вы можете столкнуться с неприятностями, когда один из ваших операторов законной удаленной системы звонит и спрашивает вас, почему он неделями не получает от вас ни почты, ни программных запросов! Од- нако, коварный нарушитель мог бы переслать копию украденных файлов обратно к вам и использовать прогрессивные средства для отправки их на законную удаленную машину.

    ПОДДЕЛКА ПОЧТЫ

Этот прием довольно хорошо известен, но мы включаем его для пол- ноты изложения. Похоже, однако, что он работает не во всех версиях системы UNIX. Он работает в System V, но не работает в XENIX, System III и Berkeley 4.2. Данный метод заключается в изменении пользова- тельской переменной среды LOGNAME. Поскольку команда mail использует ее, чтобы идентифицировать вас при отправке вам почты, меняется заго- ловок почты. Обычно это всего лишь мелкая неприятность, но вы должны уведомить об этом пользователей, чтобы они очень внимательно относи- лись к таким сообщениям, которые кажутся несвойственными для их мни- мого отправителя. СКРЫТЫЕ ИМЕНА ФАЙЛОВ ПРИ РАБОТЕ С РЕДАКТОРОМ vi Полезной практикой для обеспечения безопасности является выпол- нение случайных команд ps. Такая мера более-менее равносильна перио- дическому патрулированию с целью увидеть, не происходит ли что-нибудь опасное. Необходимо, однако, отметить, что лица, использующие редак- тор vi для несанкционированной работы, могут замести свои следы, вы- бирая такое имя редактируемого файла, чтобы оно не появлялось в рас- печатке команды ps. Самый простой способ, которым они могут это сделать - вызвать vi без указания имени файла. Тем самым vi запуска- ется с пустым файлом. Затем они могут применить команду ex для редак- тирования нужного им файла. Это убережет имя файла от распечатки ко- мандой ps, так как оно не является частью набора аргументов команды vi. Массив аргументов формируется при вызове команды vi, а не после ее запуска. Другой способ - использовать маскировку. Нарушители могут переи- меновать файл, который они хотят редактировать, в ничего не означаю- щее имя, например tmp, а потом использовать имя tmp при вызове редак- тора vi. В результате в массив аргументов занесется имя tmp. Оно и появится в распечатке команды ps.
ИМЯ: access
access

    НАЗНАЧЕНИЕ

Ищет в парольном файле все регистрационные записи, не имеющие паролей.

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

access

    ПРИМЕР ВЫЗОВА

access Выдает список всех беспарольных входов в систему

    ТЕКСТ ПРОГРАММЫ

1 : 2 # @(#) access v1.0 Show all free access logins Author: Russ Sage 4 if [ "$#" -gt "0" ] 5 then echo "access: too many arguments" >&2 6 echo "usage: access" >&2 7 exit 1 8 fi 10 grep '^[^:]*::' /etc/passwd || echo "All logins protected" ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ access? Мы уже отмечали, что записи о входе в систему в парольном файле создают возможность нарушения защиты, если с ними не связаны пароли, т.е. если поле пароля пустое. Проблема заключается в том, что в боль- ших системах парольный файл может сильно разрастись. Искать в таком файле вручную регистрационные записи, в которых отсутствуют пароли, было бы утомительным и приводило бы к ошибкам. Почему бы не поручить системе сделать за вас эту работу? ЧТО ДЕЛАЕТ access? Командный файл access использует команду grep с шаблоном поиска, описывающим регистрационную запись, не имеющую пароля. Когда такая запись попадается, она печатается в стандартный вывод. Если указанных записей не найдено, выводится сообщение "All logins protected" ("Все входы в систему защищены").

    ПОЯСНЕНИЯ

Первое, что делает access (в строках 4-8) - проверяет, правильно ли она была вызвана. Поскольку опций не предусмотрено, в командной строке ничего не должно быть. Если количество аргументов в командной строке больше нуля, то на стандартное устройство регистрации ошибок выдается сообщение об ошибке и командный файл завершается. Оператор в строке 10 выполняет поиск в парольном файле. Применя- ется утилита grep, т.к. мы используем в этой команде выражение. Если бы мы использовали фиксированную строку, более предпочтительной была бы утилита fgrep, потому что она быстрее. Выражение, задающее поиск, означает следующее: начиная с начала строки, найти все символы, от- личные от двоеточия, вплоть до обнаружения двух двоеточий подряд. Ес- ли вы заглянете в файл /etc/passwd, то увидите, что первое поле представляет собой имя (от начала строки до первого двоеточия). Затем между первым и вторым двоеточием размещается пароль. Если пароль от- сутствует, то после первого двоеточия сразу же следует второе - имен- но это соответствует нашему шаблону поиска. Поиск выполнятся в файле /etc/passwd. Если grep успешно обнаружил хотя бы одну запись, то код возврата нулевой. Если grep ничего не обнаружил, то код возврата еди- ница. Тогда активизируется последняя часть строки 10 и выводится со- общение о том, что все записи о входе в систему защищены.
ИМЯ: chkset
chkset

    НАЗНАЧЕНИЕ

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

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

chkset [-l] [dir ...]

    ПРИМЕР ВЫЗОВА

chkset -l Вести поиск, начиная с корневого каталога, поскольку каталог не указан. С помощью команды "ls -d" выдать список файлов, для которых установлен в единицу бит разрешения установки идентификатора пользо- вателя либо идентификатора группы. Результат отсортировать по имени файла. (Бит установки пользовательского идентификатора S_ISUID и бит установки группового идентификатора S_ISGID являются атрибутами защи- ты файла наряду с битами прав доступа на чтение/запись/выполнение и определены в подключаемом файле /sys/stat.h. - Примеч. перев.)

    ТЕКСТ ПРОГРАММЫ

1 : 2 # @(#) chkset v1.0 Check for set bits on Author: Russ Sage 4 FORM="-print" 5 SORT="sort" 7 if [ "`echo $1 | cut -c1`" = "-" ] 8 then case $1 in 9 -l) shift 10 FORM="-exec ls -ld {} ;" 11 SORT="sort +7";; 12 *) echo "usage: chkset [-l][file/dir ...]" >&2 13 exit 1;; 14 esac 15 fi 17 if [ "$#" -gt 0 ] 18 then SRC="$*" 19 else SRC="/" 20 fi 22 find $SRC \( -perm -4000 -o -perm -2000 \) $FORM | $SORT

    ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ

FORM Команда и опции для листинга SORT Команда и опции для сортировки результата SRC Исходный каталог, от которого нужно начинать поиск ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ chkset? Мы уже рассмотрели проблемы безопасности, которые могут возник- нуть, когда для исполняемых файлов установлен в единицу бит разреше- ния установки идентификатора пользователя. Это означает, что они мо- гут запускать интерпретатор shell с корневой или с другой привилегией высокого уровня. С той же целью может быть установлен в единицу бит разрешения установки идентификатора группы. Поэтому системный адми- нистратор должен непрерывно разыскивать и проверять все файлы в сис- теме, для которых установлены эти биты, чтобы посмотреть, не исполь- зуются ли они для несанкционированных целей. Не для всех интерпретаторов shell, нарушающих защиту, владельцем является суперпользователь (root). Один пользователь может запустить shell, владельцем которого является другой пользователь, имеющий бо- лее высокие привилегии. Это фактически предоставляет пользователю, запустившему shell, все возможности владельца файла. Найти shell-интерпретаторы, устанавливающие идентификатор поль- зователя или группы, бывает легко, а бывает и трудно, в зависимости от их авторства. Легко найти такие, которые: а) имеют необычные имена (некоторые нарушители любят выстав- лять свои достижения напоказ); б) содержат в исполняемом файле символьные строки, которые можно прочитать; в) размещены в необычном или очевидном каталоге; г) не имеют ограничений относительно того, кто может их запус- тить. Для изощренных shell-интерпретаторов характерно следующее: а) они имеют имена, похожие на обычные команды системы UNIX; б) имеют размеры файлов, которые соответствуют другим файлам, размещенным неподалеку от них; в) содержат упрятанные символьные строки, которые не так-то легко прочитать; г) имеют, возможно, специальную опцию, запускающую shell, или даже специальный пароль, необходимый для его запуска. Самые хулиганские из них являются настоящими командами системы UNIX, переделанными в корневые shell-интерпретаторы, которые затем переустанавливаются вместо первоначальных shell-интерпретаторов. Единственный надежный способ идентифицировать этот последний тип - запустить побайтовое сравнение между вашей дистрибутивной копией ко- манды системы UNIX и той версией, которая присутствует в настоящий момент в вашей системе. Конечно, перед тем как вы сможете проверить файлы со включенными битами разрешения установки пользовательского/группового идентифика- тора, вам нужно найти все такие файлы. В системе UNIX это делают две команды: find(1) и ncheck(1M). Утилита find ищет файлы по нашей точ- ной спецификации и может быть использована для поиска файлов с задан- ным набором прав доступа, включая биты установки идентификаторов. Ncheck печатает вперемешку специальные файлы и файлы с разрешенной установкой идентификатора пользователя. Это очень большой список, чтение и поиск в нем интересующих нас файлов занимает много времени. Таким образом, полезно создать командный файл, предоставляющий нам всю необходимую информацию и только такую информацию. Chkset исполь- зует команду find и ищет только файлы со включенными битами разреше- ния установки пользовательского/группового идентификатора, так что результат содержит лишь необходимую нам информацию, и этим результа- том можно сразу же воспользоваться. ЧТО ДЕЛАЕТ chkset? Chkset имеет два режима функционирования: один для сканирования всей системы в целом, а другой для сканирования указанных деревьев каталогов. Это хорошее свойство, так как сканирование каждого файла системы занимает очень много времени. Если имеется много больших дис- ковых устройств, проверка всего содержимого системы может занять це- лый час. Утилита chkset также очень сильно загружает центральный про- цессор из-за всех процессов, которые она генерирует. Указывая имена каталогов, вы можете выполнить проверку лишь на определенной области системного дерева. Отметим, однако, что поскольку chkset пользуется командой find, она сканирует не только указанный вами каталог, но и ВСЕ подчиненные каталоги. Заметим также, что chkset обнаруживает ВСЕ файлы с установленными в единицу битами установки пользовательско- го/группового идентификатора, а не только те, владельцем которых яв- ляется суперпользователь (root). Результат работы chkset можно выдать также двумя способами. Если не применять никакую опцию, то форма выдачи результата определяется командой "find ... -print", что означает полные маршрутные имена най- денных файлов. Затем эти полные имена сортируются. Если применяется опция -l, то для форматирования результата ис- пользуется команда "ls -ld", порождающая длинный формат листинга. При этом распечатываются полное указание прав доступа, число связей, вла- делец, размер и имя файла. Этот результат также сортируется по име- нам. Выбирайте тот или иной формат в зависимости от обстоятельств. Если вам нужно проверить большой участок системы и получить список подозрительных файлов, примените листинг по умолчанию (без опций), так как он компактнее и, как мы увидим позднее, занимает значительно меньше процессорного времени. Если вы хотите заняться определенным каталогом и посмотреть на его файлы подробно, воспользуйтесь опцией - l. Она предоставляет больше информации и избавляет от необходимости вручную набирать команды ls для интересующих вас файлов. Отметим, что эту команду может запустить кто угодно, а не только администратор, имеющий привилегии суперпользователя. Однако, если она запускается обычным пользователем, то команда find внутри командного файла chkset ограничена теми файлами, к которым пользователь имеет доступ на чтение. Так что вы могли бы предложить более привилегиро- ванным пользователям запускать chkset в качестве одной из их личных мер безопасности. Разумеется, если chkset запускается суперпользова- телем, то ограничения прав доступа к файлам несущественны и можно подвергнуть проверке все файлы.

    ПРИМЕР

# chkset /bin /usr/bin /lib /usr/lib Эта команда вызывает просмотр указанных каталогов. В каталогах типа /usr/lib просматриваются все подчиненные каталоги, что обеспечи- вает более тщательную проверку.

    ПОЯСНЕНИЯ

Первым делом chkset инициализирует две переменные - FORM и SORT. Переменная FORM содержит команду для выдачи результата работы команды find, а переменная SORT - команду, определяющую, что нужно сортиро- вать. В строке 7 проверяется, является ли первый позиционный параметр опцией. Если да, то оператор case (строки 8-14) смотрит, какая это опция. Если это опция "-l", то подготавливается команда для распечат- ки результата (это мы обсудим позже). Команда для утилиты sort форми- руется так, чтобы сортировка шла по полю владельца. Опция убирается из командной строки, потому что все последующие аргументы должны быть каталогами и мы захотим получить к ним доступ с помощью "$#". Если попалась опция, отличная от "-l", то это ошибка, выдается сообщение об ошибке (строка 12), и командный файл завершается. Если осталось более нуля аргументов, когда мы попадаем в строку 17, то они проверяются в цикле, чтобы убедиться, что все они являются каталогами. Если это не каталоги, на стандартное устройство регистра- ции ошибок выдается сообщение об ошибке и командный файл завершается. Если имеются параметры (т.е. каталоги), то в строке 18 в пере- менную SRC заносятся все каталоги. Если же параметров нет, то в пере- менную SRC заносится значение "/", т.е. корневой каталог, чтобы обес- печить подразумеваемую стартовую точку для поиска. Вся работа этого командного файла выполняется фактически в опе- раторе find. Команда find допускает множественное указание каталогов, которые поступают в результате чтения их из командной строки и зане- сения в переменную SRC. После того как мы указали команде find, откуда начинать поиск, мы указываем ей, что нужно искать. В данном случае нас интересуют все файлы, которые имеют включенный бит установки пользовательского либо группового идентификатора. Мы объясняем это команде find путем указа- ния прав доступа, которые требуется искать. Строка "-perm -4000" оз- начает поиск всех файлов, имеющих права доступа со включенным битом установки пользовательского идентификатора и с любыми другими вклю- ченными битами. Вы можете понимать эту запись как применение символов -заменителей - 4???. Мы ищем как установку пользовательского иденти- фикатора (-4000), так и установку группового идентификатора (-2000), поэтому две строки прав доступа соединены опцией -o, означающей "or" ("или"). (Более полное описание прав доступа в символической и вось- меричной форме приведено в chmod(1).) Следующая задача - добавить строку, хранимую в переменной FORM, в командную строку. Если опция -l не была использована, то в перемен- ной FORM хранится строка "-print", а это значит, что find будет печа- тать маршрутные имена найденных файлов. Если же -l использовалась, то переменная FORM содержит строку "-exec ls -ld {} ;". Опция -exec - это очень гибкая опция команды find, позволяющая применить любые ко- манды, которые за ней следуют, к каждому найденному файлу. В данном случае эти команды выполняют распечатку в длинном формате (-l), при- чем для каждого каталога (-d) выводится только его имя (а не содержи- мое). Именно здесь происходят наибольшие затраты ресурсов центрально- го процессора, так как для опции -l требуется системный вызов stat. Из-за того, что для каждого файла требуется команда ls, она каждый раз загружается в память и выполняется. Производится также доступ к индексному дескриптору файла на диске. Это приводит к большим наклад- ным расходам. Затем весь поток данных пропускается через утилиту sort. На са- мом деле мы хотим сделать сортировку по восьмому полю (вы можете про- верить это, выполнив команду "ls -l" и изучив ее результат). Утилита sort ПРОПУСКАЕТ указанное число полей, начиная с поля 1, являющегося по умолчанию стартовой точкой, поэтому использование записи +7 озна- чает переход к восьмому полю, которым является имя файла.

    АЛЬТЕРНАТИВНЫЙ ПОДХОД

Для поиска файлов с интересующими нас правами доступа можно при- менить другой метод, хотя и более медленный. Он основывается на том, что вместо поиска прав доступа по числу, можно искать их по символь- ной строке. Для этого нужно применять grep. Альтернативная команда выглядит так: # find $* -exec ls -ld {} \; | grep "^[^ ]*s[^ ]*" Этот вариант команды работает несколько иначе. Он находит каждый файл из указанных каталогов и применяет к каждому из них команду "ls -ld". Затем весь список данных передается по конвейеру команде grep. Grep использует для распознавания интересующих нас файлов такой шаб- лон поиска: начиная с начала строки, найти повторяющийся символ, от- личный от пробела, затем символ "s", за которым следует повторяющийся символ, отличный от пробела. Такому шаблону соответствуют все режимы прав доступа, содержащие s, будь то бит установки пользовательского идентификатора или бит установки группового идентификатора. Не су- щественно, входят ли в данный режим "r", "w", "x" или "-". Убедив- шись, что пробелов нет, мы обеспечиваем соответствие шаблона только полю прав доступа. Важно, чтобы после s следовали символы, отличные от пробелов, так как s может встретиться либо в порции прав доступа, относящейся к владельцу, либо в групповой порции и нигде больше. Мы не очень рекомендуем этот метод, поскольку он привлекает очень интенсивную обработку. Однако, если бы мы не испробовали этот метод первым, мы бы не оценили, что использование команды find со строками "-perm" является более предпочтительным. В силу невероятной гибкости системы UNIX, имеется очень много различных способов выпол- нения одной и той же работы, и многие из этих способов могут давать одинаково хорошие результаты, но за разную цену в смысле быстродейс- твия и процессорных затрат. Перед тем как применять универсальную утилиту вроде grep для решения задачи, рассмотрите, нет ли другой ко- манды со встроенной опцией распознавания по шаблону. Скорее всего, применение такой команды окажется оптимальнее и гораздо быстрее, чем использование grep. С другой стороны, иногда вам нужно быстро решить нечасто встречающуюся задачу, не слишком заботясь об эффективности решения.
ИМЯ: suw
suw

    НАЗНАЧЕНИЕ

Просматривает протокольный файл команды su и печатает имена всех пользователей, которые нелегально превратились в суперпользователя при помощи команды su (substituted user, замененный пользователь).

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

suw [-m] [sulog]

    ПРИМЕР ВЫЗОВА

suw Запуск в режиме по умолчанию, проверка файла /usr/adm/sulog и выдача записей о нарушителях в стандартный вывод.

    ТЕКСТ ПРОГРАММЫ

1 static char id[]="@(#)suw v1.0 Author: Russ Sage"; 3 # include 5 # define FALSE 0 6 # define TRUE 1 7 # define MATCH 0 8 # define BSIZ 80 10 main(argc,argv) 11 int argc; 12 char *argv[]; 13 { 14 register int alert, c, mail, n; 15 FILE *fp1, *fp2; 16 char *p, *uname, line[BSIZ], tmp[BSIZ], 17 *log = "/usr/adm/sulog"; 19 static char *legal[] = {sage-root\n","root-root\n",NULL}; 20 static char *adm[] = {"sage",NULL}; 23 mail = FALSE; 25 if (argc > 1 && argv[1][0] == '-') 26 switch (argv[1][1]) 27 { 28 case 'm': 29 mail = TRUE; 30 --argc; 31 ++argv; 32 break; 33 default: 34 fprintf(stderr,"suw: invalid argument %s\n", argv[1]); 35 fprintf(stderr,"usage: suw [-m] [sulog]\n"); 36 exit(1); 37 } 39 if (argc == 2) 40 log = *++argv; 42 if ((fp1 = fopen(log,"r")) == NULL) 43 { 44 fprintf(stderr,"suw: error opening %s\n",log); 45 fprintf(stderr,"usage: suw [-m] [sulog]\n"); 46 exit(1); 47 } 49 sprintf(tmp,"/tmp/suw%d",getpid()); 50 if ((fp2 = fopen(tmp,"w+")) == NULL) 51 { 52 fprintf(stderr,"suw: error opening %s\n",tmp); 53 fprintf(stderr,"usage: suw [-m] [sulog]\n"); 54 exit(1); 55 } 57 while (fgets(line,sizeof(line),fp1) != NULL) 58 { 59 p = line + 15; 60 if (*p == '+') 61 { 62 p = p + 2; 63 while (*p != ' ') p++; 64 p++; 65 uname = p; 66 while (*p && *p++ != '-') 67 continue; 69 if (strcmp (p,"root\n") == MATCH) 70 { 71 alert = TRUE; 72 for (n=0; legal[n] != NULL; n++) 73 if (strcmp (uname,legal[n]) == MATCH) 74 { 75 alert = FALSE; 76 break; 77 } 78 if (alert) 79 fprintf(fp2,"Illegal --> %s", line); 80 } 81 } 82 } 84 if (mail) 85 { 86 fclose(fp2); 87 for (n=0; adm[n] != NULL; n++) 88 { 89 sprintf(line,"cat %s | mail %s",tmp,adm[n]); 90 system(line); 91 } 92 } 93 else 94 { 95 rewind(fp2); 96 while ((c = getc(fp2)) != EOF) 97 putc(c, stdout); 98 fclose(fp2); 99 } 101 fclose(fp1); 102 unlink(tmp); 103 } ОПИСАНИЕ ЗАЧЕМ НАМ НУЖНА ПРОГРАММА suw? Вы помните, что команда su, позволяющая пользователям изменять свою индивидуальность (и права доступа) может быть источником проблем безопасности. Система хранит протокол всех транзакций su в файле sulog. Хотя более опытные нарушители могут уметь затирать свои следы, файл sulog полезен для отслеживания потенциальных лазеек в системе защиты. Этим способом можно поймать многих нарушителей-любителей. Ес- тественно, мы хотим автоматизировать этот процесс, чтобы система вы- полняла проверку и сигнализировала нам при обнаружении чего-либо опасного. Кроме того, данная программа демонстрирует методику, кото- рую можно использовать для отслеживания других протокольных файлов. ЧТО ДЕЛАЕТ suw? Программа suw читает и анализирует протокольные файлы команды su. Каждое успешное превращение в суперпользователя при помощи коман- ды su, обнаруженное в протокольном файле, сверяется со списком разре- шенных суперпользователей. Если пользователю не разрешено быть супер- пользователем, то конкретная запись о нем печатается, чтобы оповестить администратора. По умолчанию записи о нарушителях печатаются в стандартный вы- вод. Протокольным файлом по умолчанию является /usr/adm/sulog. Если применяется опция -m, то записи о нарушителях рассылаются по почте администраторам, занесенным в предопределенный список. Если нужно проверить другой протокольный файл, например /usr/adm/Osulog, то его имя можно указать в командной строке.

    ПРИМЕРЫ

1. # suw -m Проверить файл /usr/adm/sulog и разослать записи о нарушителях администраторам, определенным в тексте программы. 2. # suw /usr/adm/Osulog Проверить файл /usr/adm/Osulog и напечатать записи о нарушителях в стандартный вывод.

    ПОЯСНЕНИЯ

В самом начале программы определяются и инициализируются все пе- ременные и списки. В строке 14 определены два флага: alert (сигнал тревоги) и mail (почта). Они имеют значение TRUE или FALSE. В этой программе используются два файла: протокольный файл команды su, кото- рый вы выбираете, и временный файл. Поскольку мы собираемся применять некоторые подпрограммы стандартного ввода-вывода (stdio), мы пользу- емся указателями на файлы fp1 и fp2, а не дескрипторами файлов. При- меняется два буфера: один для зачитывания в него данных из протоколь- ного файла команды su, а другой - для хранения имени временного файла. Первоначально именем протокольного файла является sulog. Если вместо него используется другое имя файла, то переменная log переус- танавливается на это имя. Затем инициализируются предопределенные списки разрешенных су- перпользователей и администраторов. В нашем примере двумя разрешенны- ми суперпользователями являются sage (sage-root) и root (root-root). Для того чтобы приспособить это для вашей системы, поместите здесь имена людей, которым вы хотите разрешить пользоваться командой su для превращения в суперпользователей. Список администраторов также должен содержать соответствующие имена. В данном примере в список админист- раторов входит одно имя - sage. Это значит, что sage будет единствен- ным, кто получит почту, если указана почтовая опция. Подразумеваемое состояние рассылки почты устанавливается в стро- ке 23 на значение FALSE, т. е. почты нет. Это делается для того, что- бы после разбора командной строки переменная mail имела правильное значение. Далее выполняется проверка на ошибки. Первая проверка выглядит несколько странной, но на самом деле вполне понятна. Переменная argc возвращает число аргументов командной строки, а argv указывает на массив, содержащий сами аргументы, каждый из которых сам является массивом символов. Если командная строка вообще имеет какие-либо ар- гументы (argc > 1) и первым символом первого аргумента (argv[1][0]) является дефис, то проверяется, корректная ли это опция. С целью про- верки второго символа первого аргумента используется оператор case. Если аргументом является символ m, то флаг mail устанавливается в состояние TRUE, число аргументов (argc) уменьшается на единицу, а указатель на массив аргументов (argv) на единицу увеличивается. Это служит той же цели, что и удаление аргументов из командной строки в командных файлах интерпретатора shell. Напомним, что argv является в действительности массивом указателей, а массивы трактуются точно так же, как строки, с базовым адресом и смещением. Поэтому argv[0] == argv, и argv[1] == ++argv. Если опция отличается от -m, то в стан- дартный вывод печатается сообщение об ошибке и программа завершается. В строке 39 проверяется счетчик аргументов, чтобы понять, есть ли еще один аргумент. Напомним, что argv всегда на единицу отстает от argc, потому что само имя команды является первым аргументом массива. Следовательно, для того чтобы получить второй аргумент, мы должны пе- рейти к следующей позиции (*++argv). Если аргумент имеется, он должен быть именем протокольного файла, и это имя заносится в переменную log. Далее мы пытаемся открыть файлы, используемые в программе. Сна- чала открывается для чтения протокольный файл. Если это не срабатыва- ет (возвращен нулевой указатель), то печатается сообщение об ошибке и программа завершается. Затем в строке 49 создается имя временного файла с добавлением к нему идентификатора процесса, чтобы гарантиро- вать уникальность имени файла. Этот файл открывается на чтение и за- пись. Если эта операция не удается, печатается сообщение об ошибке и выполнение завершается. В строках 57-103 заключен главный цикл. Управляющая часть глав- ного цикла определяется тем, все ли данные прочитаны из протокольного файла. Если больше никакие байты прочитать нельзя, то fgets (строка 57) возвращает нулевой указатель, что завершает цикл while. Когда строка данных читается из протокольного файла, эти данные помещаются в массив line. Мы применяем указатель на символ для прохода вдоль строки и поиска записи о нарушителе. Изучая запись в вашем файле sulog, вы можете увидеть, где нахо- дятся интересующие нас поля. Сначала указатель смещается на 15 симво- лов от начала строки. Это позиция флага, определяющего, была ли ус- пешной команда su. Если она была успешной, указатель увеличивается на два, чтобы пропустить пробел и установить указатель на имени термина- ла. Это имя имеет переменную длину, поэтому мы ориентируемся по про- белу в конце имени. В строках 63-64 применяется цикл while, который заставляет указатель пропустить все символы, отличные от пробела. За- тем он увеличивается на единицу еще раз, чтобы пропустить пробел меж- ду именем терминала и строкой имени пользователя. В этот момент указатель находится в последнем поле строки. Ука- затель на это поле помещается в переменную uname, чтобы затем ее мож- но было использовать для сравнения строк. Следующий цикл while (стро- ки 66-67) проходит по строке, пока не попадет на символ дефиса. Цикл while выполняется до тех пор, пока указатель не указывает на конец строки (т.е. на нелевой указатель, *p == '\0') и еще не указывает на дефис. Поскольку в цикле используется p++, то когда мы попадаем на нулевой указатель, цикл завершается, причем p указывает на следующий символ. Выполняется проверка, был ли суперпользователем тот, кто приме- нил команду su. Если был, то взводится флаг alert, и мы должны прове- рить, разрешенный ли это суперпользователь. Цикл for (строки 72-77) пробегает по всем именам в массиве, содержащем имена разрешенных пользователей, до тех пор, пока мы не обнаружим последнюю запись, ко- торая является пустой. Строка с именем пользователя, установленная предварительно, сверяется с каждой записью в массиве разрешенных имен. Если они совпадают, то мы можем предположить, что с данным при- менением команды su все в порядке. Тогда мы выключаем флаг alert и завершаем сравнение имен. Если же по окончании цикла флаг alert все еще взведен, значит имя не содержится в списке разрешенных - это, возможно, нарушитель. Вся запись целиком записывается во временный файл и управление передается назад в начало цикла для следующей опе- рации чтения. Когда все данные прочитаны, цикл while завершается. В строке 84 проверяется, взведен ли флаг mail. Если да, то временный файл закры- вается (чтобы сработала команда cat) и выполняется еще один цикл for (строки 87-91). Он перебирает всех администраторов и завершается, когда попадает на нулевой указатель в конце массива. Для каждого из обозначенных администраторов конструируется команда mail и посылается в систему UNIX при помощи системного вызова (строка 90). Метод отп- равки по почте временного файла заключается в применении к этому фай- лу команды cat и передаче данных по конвейеру команде mail. Если флаг mail выключен, то временный файл нужно напечатать в стандартный вывод, а не отправить по почте. Для того чтобы сделать это как можно быстрее, временный файл (который пока еще открыт) "пе- рематывается" (мы позиционируемся в его начало) и посимвольно пропус- кается по циклу. Обратите внимание, что данный цикл является сердце- виной команды cat, как описано на странице 153 книги Кернигана и Ритчи "Язык программирования Си" (B.W.Kernighan, D.M.Ritchie. The C Programming Language). После того как временный файл напечатан, он закрывается. Наконец, закрывается протокольный файл и удаляется вре- менный файл.

    * ГЛАВА 10. Смешанные приемы *

Введение Способы преобразования в языке shell conv Модули преобразования dtoh dtoo htod htoo Тонкости bc otod otoh Приемы языка shell для обеспечения гибкости программ Хитрости языка shell Читайте ввод с клавиатуры, пока находитесь в цикле, присоединенном к програмному каналу Запуск дочернего языка shell Уровни языка shell и ввод-вывод Встроенный ввод С редактором ed С файлом a.out C архивами языка shell Управление статусом цикла Фильтры и синтаксис Недостатки/особенности программирования на языке shell Программа для перенаправления ошибки Некорректный код возврата Хитрости редактора Vi Возвращение в язык shell Поддержка Escape Макросы Команда "One-Liners" - крошечная, но мощная . - 2 - Введение Эта книга является итогом многолетней работы по подбору и развитию инструментальных средств ОС UNIX. Многие вещи, которые не хотелось бы оставлять без внимания, не вписались в контекст предыдущих глав. Это и законченные процедуры, подобные представленным ранее, и небольшие, но очень мощные фрагменты программ. Кроме того, высказаны некоторые полезные идеи и представлены методы обработки общих ситуаций на языке shell. Способы преобразования Поскольку компьютеры и их резидентные утилиты используют при работе разные системы счисления, часто возникает необходимость преобразования оснований систем счисления. Эти преобразования обеспечиваются хорошо знакомыми специалистам командами UNIX bc (калькулятор произвольной точности) и dc (которая предположительно расшифровывается как настольный калькулятор ("desk calculator")). Большинство из существующих возможностей либо носят очень ограниченный характер, либо их тяжело использовать в ряде ситуаций, поэтому будет рассмотрен вопрос как использовать существующие возможности UNIX, чтобы любое преобразование было как можно более легко осуществимым.
Название: conv
conv Переводит числа из одной системы счисления в другую Назначение: Обеспечивает возможность преобразования основания системы счисления Вызов conv Пример вызова: $conv Вызвать главное меню различных преобразований 2 Выбрать опцию 2 ( из шестнадцатиричной в десятичную) FFF Ввести шестнадцатиричное число FFF. На выходе программы получим десятичный эквивалент Исходный текст для функции conv 1 : 2 # @(#) conv v1.0 Преобразование основания системы счисления, используя shell Автор: Russ Sage 3 4 while : 5 do 6 echo " 7 8 Преобразование оснований 9 ------------------------ 10 1 - Десятичное в шестнадцатиричное 11 2 - Шестнадцатиричное в десятичное 12 3 - Десятичное в восьмеричное 13 4 - Восьмеричное в десятичное 14 5 - Восьмеричное в шестнадцатиричное 15 6 - Шестнадцатиричное в восьмеричное 16 17 enter choice (1-6, <>): \c" 18 read CHOICE 19 20 case $CHOICE in 21 "") exit;; 22 1) echo "\пВведите десятичное число (<> to exit): \c" 23 read DEC 24 if [ "$DEC" = ""] 25 then exit 26 fi 27 HEX='. dtoh' 28 echo "\n${DEC}d = ${HEX}x";; 29 2) echo"\nВведите шестнадцатиричное число в верхнем регистре (<> to exit): \c" 30 read HEX 31 if [ "$HEX" = ""] 32 then exit 33 fi 34 DEC='. htod' 35 echo "\n${HEX}x= ${DEC}d;; 36 3) echo "\nВведите десятичное число в верхнем регистре (<> to exit): \c" 37 read DEC 38 if [ "$DEC" = ""] 39 then exit 40 fi 41 OCT='. dtoo' 42 echo "\n${DEC}d = ${OCT}o";; 43 4) echo "\nВведите восьмеричное число (<> to exit): \c" 44 read OCT 45 if [ "$OCT" = ""] 46 then exit 47 fi 48 OCT='. otod' 49 echo "\n${OCT}o = ${DEC}d";; 50 5) echo "\nВведите восьмеричное число (<> to exit): \c" 51 read OCT 52 if [ "$OCT" = ""] 53 then exit 54 fi 55 HEX='. otoh' 56 echo "\n${OCT}o = ${HEX}x";; 57 6) echo "\nВведите шестнадцатиричное число в верхнем регистре (<> to exit): \c" 58 read НЕХ 59 if [ "$НЕХ" = ""] 60 then exit 61 fi 62 OCT='. htoo' 63 echo "\n${HEX}x = ${OCT}o";; 64 *) echo "\n$CHOICE-неизвестная команда";; 65 esac 66 done Переменные окружения CHOICE - Выбор команд из главного меню DEC - Выдает десятичное значение как результат преобразования HEX - Выдает шестнадцатиричное значение как - 4 - результат преобразования OCT - Выдает восьмеричное значение как результат преобразования Описание Зачем нам нужна функция conv ? Выполнение числовых операций большого объема в командных файлах языка shell - это далеко не самая хорошая идея. Командные файлы явля- ются весьма медленными сами по себе, а выполнение математических опе- раций еще больше замедляет их работу. Однако, процедуры языка shell имеют математические возможности, и Вы, возможно, захотите ими воспользоваться. Если Вам нужно преобразовать несколько чисел в про- цессе написания программы, то для этой цели достаточно удобно вызвать процедуру языка shell. Поскольку conv - это программа, управляемая меню, Вам не придется беспокоиться о запоминании сложного синтаксиса, котрый используют некоторые системные утилиты преобразования. Что делает conv? Это инструментальное средство обеспечивает возможность перевода чисел из одной системы счисления в другую. Можно переводить десятичные, шестнадцатиричные и восьмеричные данные. Число, записанное в одной из этих форм, может быть переведено в любую из двух оставшихся форм. Режим работы программы выбирается из главного меню. В меню есть шесть пунктов. После того как Вы выбираете число между 1 и 6, программа просит Вас ввести число которое Вы хотите преобразовать. Происходит преобразование и на выходе программы Вы получаете два значения - число, которое Вы преобразовываете и число, к которому оно было преобразовано. Преобразования осуществляются путем вызова внешних процедур, о которых будет идти речь дальше в этой главе, так что перед запуском conv необходимо убедиться, что Вы включили их в вашу систему и разместили в том же каталоге, что и conv. Если Вы введете команду, не вошедшую в вышеупомянутый перечень, то будет выдано сообщение об ошибке и опять будет выведено главное меню. Пояснение Строки 4-66 - это один большой бесконечный цикл while. Мы используем бесконечный цикл, чтобы в случае ошибочного ввода программа вернулась в главное меню для повторного ввода. Для того, чтобы выйти из программы, нужно прервать цикл, т.е. выйти из цикла. Строки 6-17 печатают меню и выдают подсказку для выбора. Если Вы просто нажмете "Ввод", программа завершит свою работу. Строка 18 читает ввод с клавиатуры, и строки 20-65 выполняют выбор по условию для этой величины. Если получен нулевой (пустой) ввод, то программа завершает свою работу. Строки 22-28 осуществляют перевод чисел из десятичной в шестнадцатиричную системы счисления. Поскольку все модули перевода отвечают одному и тому же образцу, то детально мы рассмотрим только данный модуль. Подсказка запрашивает число в строке 23. В строках 24-26 проверяется, не было ли введенное значение пустым. Строка 27 выглядит несколько загадочно, вызывая один из внешних командных файлов dtoh для преобразования десятичных чисел в шестнадцатииричные. Обратите внимание на то как одна программа выполняет другую. Командный файл dtoh запускается, используя команду ".". Это означает : "Выполните программу, используя тот же shell". Процедура dtoh использует переменную DEC для ввода числа и выдает - 5 - преобразованное число на стандартный вывод. Чтобы записать это число в переменную, мы делаем присвоение, потом запускаем программу, используя командную подстановку. Строка 28 выдает на экран первоначальное десятичное число и шестнадцатиричное, к которому оно было преобразовано. Варианты 2, 3, 4, 5 и 6 работают аналогично. Единственное, что меняется - это имя переменной, которое соответствует типу преобразования и название командного файла (скрипта), который вызывается для этого преобразования. Модули преобразования Теперь давайте рассмотрим отдельно каждый из модулей перевода. Эти модули или командные файлы языка shell используют команду UNIX bc, чтобы осуществлять преобразования оснований систем счисления. Нельзя сказать, что команда bc - это наиболее простой и удобный способ перевода, но тем не менее она работает, и единственное, что нам нужно, - это изучить ее и поместить в командный файл.
Название : dtoh
dtoh Десятичные в шестнадцатиричные. Назначение: Преобразовывает входные десятичные числа в выходные шестнадцатиричные числа. Синтаксис: $DEC="decimal_number"; HEX='.dtoh' Пример вызова $DEC="25";HEX='.dtoh' $echo $HEX Присвоить DEC начальное значение 25, вызвать dtoh для его преобразования и записать результат в HEX. Вывести результаты на экран. Исходный текст для dtoh 1 : 2 # @(#) dtoh v1.0 Преобразование языка shell--десятичные в шестнадцатиричные Автор: Russ Sage 3 4 bc < HEX $ echo "шестнадцатиричное число: 'cat HEX'" Обозначение () запускает вызываемую процедуру в дочернем языке shell. Используя "." для ее выполнения, мы по прежнему имеем доступ к переменной DEC. Стандартный вывод перенаправляется в HEX. Эхо сопровождение получает значение, используя командную подстановку. Результат cat помещается в эхо предложение.
Название: dtoo
dtoo Десятичные в восьмеричные Назначение Переводит входные десятичные числа в выходные восьмеричные. Синтаксис: DEC="decimal_number"; OCT='.dtoo' Пример вызова $DEC="16";OCT='.dtoo' - 7 - $echo $OCT Присвоить DEC начальное значение 16, вызвать dtoo для ее преобразования и записать результат в OCT. Вывести результаты на экран. Исходный текст для dtoo 1 : 2 # @(#) dtoo v1.0 Преобразование языка shell--десятичные в восьмеричные Автор: Russ Sage bc < Название: htod
htod Шестнадцатиричные в десятичные Назначение Переводит входные шестнадцатиричные числа в выходные десятичные. Синтаксис: HEX="hex_number"; DEC='.htod' Пример вызова $HEX="1EAC" ; DEC='.htod' $echo $DEC Присвоить HEX шестнадцатиричное значение, преобразовать его, напечатать результаты. Исходный текст для htod 1 : 2 # @(#) dtoo v1.0 Преобразование языка shell--шестнадцатиричные в десятичные Автор: Russ Sage bc < Название: htoo
htoo Шестнадцатиричные в восьмеричные Назначение Переводит входные шестнадцатиричные числа в выходные восмеричные. Синтаксис: HEX="hex_number"; OCT ='.htoo' - 8 - Пример вызова $HEX="F1E" ; $OCT='.htoo' $echo $OCT Присвоить HEX шестнадцатиричное значение, преобразовать его, напечатать восьмеричное число. Исходный текст для htoo 1 : 2 # @(#) htoo v1.0 Преобразование с помощью языка shell -- шестнадцатиричные в восьмеричные Автор: Russ Sage bc < Название: otod
otod Восьмеричные в десятичные Назначение: Переводит входные восьмеричные числа в выходные десятичные. Синтаксис: OCT="octal_number"; DEC = '.otod' Пример вызова $OCT="777" ; $DEC='.ot $echo $DECod' Присвоить OCT восьмеричное число, преобразовать его в десятичное, напечатать результат. Исходный текст для otod 1 : 2 # @(#) otod v1.0 Преобразование языка shell--восьмеричные в десятичные Автор: Russ Sage - 9 - bc < Название: otoh
otoh Восьмеричные в шестнадцатиричные Назначение: Переводит входные восьмеричные числа в выходные шестнадцатиричные. Синтаксис: OCT="octal_number"; HEX = '.otoh' Пример вызова $OCT="777" ; $DEC='.otoh' $echo $HEX Присвоить OCT восьмеричное число, преобразовать значение OCT в шестнадцатиричное, запуская otoh. Присвоить результат преобразования HEX, отобразить значение HEX. Исходный текст для otoh 1 : 2 # @(#) otoh v1.0 Преобразование с помощью языка shell -- восьмеричные в шестнадцатиричные Автор: Russ Sage bc < idfile Команда ls запускается как дочерний shell, используя обозначение (). Дочерний shell помещается в фоновый режим, используя символ & . Когда результат процесса id отображен, он направляется в файл ошибок дочернего языка shell, который его выполняет. Мы просто перенаправля- ем стандартную ошибку в файл и получаем число! Теперь мы можем сде- лать что-нибудь типа: $ kill -9 'cat idfile' где процесс id, переданный kill, генерируется из команды cat, которая печатает процесс id, захваченный ранее. Это может дать программам опцию "kill self", где они могут отслеживать их id, чтобы вам не пришлось это делать. Программа watch, которую мы видели в главе 6 делает нечто подобное. Встроенный ввод Редактор vi удобен до тех пор, пока Вам не нужно делать построчное редактирование текста или редактирование в командном режиме. Sed тоже неплохой редактор, но в нем не предусмотрена возможность перемещения по файлу. Sed может перемещаться только вперед по файлу до конца файла. Все наши проблемы может решить скромный редактор ed. С редактором ed Еd является интерактивным редактором и в нем есть все необходимое для обработки выражений. Поскольку он читает стандартный ввод для своих команд, мы можем помещать в stdin встроенный текст для - 12 - управления собственно редактором. Еd читает команды, как если бы они были даны с клавиатуры. Он не знает, что запущен в командном режиме. Это открывает совершенно новый способ использования мощи интерактивного редактирования в командном режиме. В качестве примера рассмотрим следующую программу. Помните, что все специальные символы в языке shell должны быть заключены в кавыч- ки, например $. Если они не заключены в кавычки, то ввод будет некор- ректным. ed file << -! 1, \$s/^ *// w q ! В этом примере редактируется файл под названием "file" и над ним выполняется несколько команд. Первая команда говорит "От первой строки до последней, для каждой строки, имеющей пустые символы в начале строки, за которыми следует любое количество таких же символов, заменить эти символы "ничем". Запишите файл и выйдите." Эта процедура удаляет пробелы из начала строки. С файлом a.out Возможность встроенного текста также можно использовать, чтобы автоматизировать запуск программ. Вам нужно записать входные данные, необходимые, чтобы программа выполняла желаемую задачу, и поместить их в текст программы. (Это нечто вроде построения макросов клавиатуры для прикладных программ для PC). В следующем примере исполняемый файл a.out запускается как дочерний shell. Его ввод берется из самого файла, а вывод передается команде more, так что мы можем сделать постраничный вывод. $ (a.out < text > input > lines > ! ) | more Это можно напечатать непосредственно с клавиатуры. Мы используем символы скобок, поскольку, если непосредственно печатать этот код, shell будет выдавать подсказку PS2 вплоть до знака !, затем выполнит команду. Единственное, что мы можем сделать, чтобы он не вышел автоматически - это продолжать запрашивать ввод, опуская его на уровень ниже. C архивами языка shell Архивы языка shell - это один из самых простых способов упако- вать текст в самоустанавливающуюся программу. Идея состоит в том, что мы используем командный файл языка shell, чтобы упаковать некоторый текст. Этот текст может быть документом, командным файлом или даже исходным текстом программы. Мы используем конструкцию встроенного текста, чтобы передать текст в shell, который потом пересылает его в предопределенные файлы. Ниже приведен пример архива, который может быть в файле. $ cat archive - 13 - # # Это архивный файл текстовых файлов 1, 2 и 3 # echo "извлекаем текстовый файл 1" cat > text1.sh << ! # # Это пример текстового файла 1 # who | sort ! echo "извлекаем текстовый файл 2" cat > text2 << ! Это содержимое второго файла. Это не программа, а просто текст. Заметьте, что ему не нужно строк комментария, потому что запущенный shell знает, что это ввод. Но не пытайтесь запускать text2, т.к. у Вас все равно ничего не получится. ! echo "извлекаем текстовый файл 1" cat > text3.c << ! /* Это содержимое файла 3, Си программа */ main() { printf("hello world"); } ! # # конец архивного файла # При выполнении архив проходит через три команды cat. Первая ко- манда cat создает файл text1.sh (командный файл языка shell), text2 (обычный текст) и text3.c (Си-программа). Все это выполняется после того, как Вы наберете на клавиатуре "archive". Это удобно, если нужно перенести текст в другое место. Вместо того чтобы пересылать три фай- ла, нам нужно переслать один. Вместо трех файлов, соединенных вместе, у нас три отдельно упакованнных файла, каждый из которых восстанавли- вает себя при запуске архива. Таким образом, нам не придется гадать, пытаясь представить какой текст в какой файл попадет. Управление статусом цикла Иногда условные выражения цикла нужно подбирать специальным образом, чтобы они отвечали нашим потребностям. Это происходит не часто, однако бывают ситуации, когда Вы можете захотеть использовать определенный синтаксис. В таблице 10-1 приведены три разных способа сделать условие цикла while "истинным". Помните, что shell ищет успешный статус выхода - статус (0) из последней синхронно выполняемой команды. Таблица 10-1 Способы заставить цикл быть "истинным" +--------------+----------------------------------------+ | Цикл | Условие со значением "истина" | - 14 - +--------------+----------------------------------------+ |while true |True - это команда в /bin, которая воз-| | |вращает статус 0 | |while[1 -eq 1]|Мы используем здесь тестовую команду, | | |чтобы возвратить статус 0 | |while : |Мы используем встроенное предложение | | |shell'а, чтобы возвратить статус 0 | +--------------+----------------------------------------+ Фильтры и синтаксис Ранее в этой книге уже шла речь о фильтрах. Не все команды являются фильтрами или могут быть использованы в качестве фильтров. Вспомните определение фильтра - это команда, которая берет ввод из аргументов командной строки, если они есть. Иначе ввод читается из стандартного ввода. Почему все команды не могут действовать как фильтры? Потому, что они не предназначены для этого. Возьмем, например, ls. Что делает ls? Она выдает список файлов в текущем каталоге. Если мы говорим "ls file", он выдает информацию только для этого файла. Если мы говорим "echo file | ls", ls не дает информацию о файле, но выдает список файлов в текущем каталоге, потому что ls не смотрит в стандартный ввод, если в командной строке нет аргументов. Один важный момент, связанный с фильтрами - это способ их вызова. Если Вы помещаете имя файла в командную строку, фильтр открывает файл и читает данные. Помните, что фильтры хотят читать данные. Если Вы присоединяете стандартный ввод к фильтру, он думает, что то, что он читает из программного канала- это данные. Если Вы дадите фильтру имена файлов, Вы не получите того результата, который ожидаете. Давайте рассмотрим несколько примеров. Команда UNIX wc - это фильтр. Мы можем вызывать ее как "wc file1 file2 file3", чтобы она подсчитала слова в трех файлах. Что было бы, если бы мы сказали: "ls file1 file2 file3 | wc" ? Wc подсчитала бы сумму символов, которые выдала бы ls. В данном случае в строке file1, file2, и file3 - 15 символов. Как нам получить реальные файловые данные, а не имена файлов в wc? Изменяя способ, которым мы вызываем wc: cat file1 file2 file3 | wс Путем предварительного соединения файлов, данные передаются на вход wc, и wc суммирует подсчитанные суммы символов, содержимого файлов. Та же самая концепция применима для всех команд фильтров. Еще одной подобной командой является awk. Мы можем сказать "awk file", и команда прочитает содержимое файла, или "cat file | awk", и получим такой же результат. Мы не можем использовать синтаксис "ls file | awk", т.к. awk выполняет свою программу только над символами в имени "file". Недостатки/особенности программирования на языке shell В этом разделе мы рассмотрим некоторые недочеты языка shell. До конца пока не ясно, почему проявляются эти дефекты или особенности. Так уж shell работает и такой уж он на самом деле. Программа для перенаправления ошибки 1 : 2 # @(#) перенаправление ошибочного значения переменной в цикл, присоединенный к программному каналу 3 4 N=1 5 echo "начальное значение N = $N" 6 7 echo "1\n2\n3" | while read LINE 8 do 9 N=2 10 echo "значение в цикле N = $N" 11 done 12 13 echo "конечное значение N = $N" Программа показывает, что различные присвоения, сделанные в до- черних языках shell, не распространяются на их родителей. Строка 4 присваивает N начальное значени 1. Затем значение N отображается в строке 5 для проверки. Вся хитрость этой программы заключена в строке 5. Мы отправляем символы "1 новая строка 2 новая строка 3" в прог- раммный канал и даем это на вход циклу while. Таким образом, мы заставляем цикл выполнить три итерации. Присоединяя вывод к программ- ному каналу, мы создаем дочерний shell, чтобы выполнить цикл while. Внутри цикла while мы изменяем значение N и печатаем его для провер- ки. В конце цикла мы печатаем окончательное значение N. Оно больше не равно 2 как это было внутри цикла, а равно 1, как это было после первого присвоения. Ниже представлен пример прогона этой программы. $ redir начально значение N = 1 значение в цикле N = 2 значение в цикле N = 2 значение в цикле N = 2 конечное значени N = 1 Это показывает, что значение переменной передается вниз дочернему языку shell, но изменения в дочернем shell не передаются родительско- му. Некорректный код возврата Откуда shell знает, являются ли корректными те или иные коды возврата? На это сложно ответить. Иногда оказывается, что код ошибочен, когда ошибку содержит ваша программа. Например: 1 : 2 # @(#) ошибка кода возврата 3 4 echo "Введите команду : \с" 5 read CMD 6 7 eval $CMD 8 echo "\$? = $?" 9 10 if [ $? -eq 0 ] 11 then echo хороший выход - $? 12 else echo плохой выход - $? 13 fi Программа начинается с того, что в строке 4 Вам выдается подсказка для введения команды. Команда читается, строка 7 оценивает не требуются ли ей дополнительные переменные и выполняет ее. Помните, что нам нужно оценить параметр в том случае, если кто-нибудь сказал что-то типа "echo $HOME". Если команды eval нет, то печатается - 16 - литеральная строка $HOME. После команды eval печатается действительное значение $HOME. Так что нам приходится использовать команду eval в этой ситуации. После того как команла выполнена строка печатает статус выхода, ссылаясь на $?. Это совершенно нормально. Строка 10 затем использует тестовую команду, чтобы программа разветвилась в зависимости от кода возврата. Это и есть то место, где ошибка. Величина, которую видит тестовая команда не совпадает с той, которую печатает эхо. $ status введите команду: ls -z ls : illegal option --z usage : -1ACFRabcdfglmnopqrstux [files] $? = 2 хороший выход - 0 Это показывает, что ls запустили в ошибочном режиме. Ls напечатал свое сообщение об ошибке и возвратил код возврата равный 2. Однако тестовая команда видит $? как значение 0 и выбирает ветвь истина. Можете ли вы найти ошибку в строке 8 программы ? Это хорошая нота для окончания раздела об ошибках. Хитрости редактора Vi Одна из самых замечательных особенностей UNIX - это возможность выйти из програм так, чтобы Вы могли запускать другие команды вне языка shell. Это было разработано внутри UNIX и является простым и мощным средством. Использование некоторых приемов, описанных ниже мо- жет сделать разработку программы более простой и быстрой. Возвращение в shell Возвращение в shell - очень полезная возможность в редакторе vi. Вы можете записать Вашу программу в редакторе, выйти из него, запустить программу, вернуться назад в редактор и т.д. Этот цикл редактирование - трансляция - проверка может быть выполнен из редактора. С этими возможностями входа и выхода из редактора Вы можете закончить сеанс работы без реального уничтожения редактора. Редактор vi также является редактором ex. Vi - это визуальная часть ex. Следовательно, Вы можете выйти в shell двумя путями. Первый - используя переменную sh, которая установлена в редакторе ex. Вы можете набрать : sh пока вы в vi или просто "sh", если Вы в ex. Редактор прямо покидает shell, который Вы определили в переменной sh. Откуда редактор знает какой shell Вы запускаете? Это можно определить по регистрационной переменной окружения языка shell. Если Ваш shell - /bin/sh, а Вы хо- тите запустить /bin/shV, Вы можете переустановить значение переменной, напечатав ":set sh=/bin/shV" Другой способ выхода из vi - это с использованием синтаксиса: :!sh где "sh" дает Вам shell (/bin/sh). Обратите внимание, что происходит. Вы запускаете shell (запускаемый по :!), которому дана команда запустить shell (:!sh). Когда Вы, наконец, запукаете этот shell, получается, что у Вас запущен лишний shell. Это очень наглядно представлено в листинге ps, приведенном ниже - 17 - UID PID PPID C STIME TTY TIME COMMAND russ 35 1 0 Jul 5 co 0:50 -shv russ 1233 35 0 04:30:15 co 0:57 vi file russ 1237 1233 0 04:43:13 co 0:01 sh -c sh russ 1238 1237 0 04:43:15 co 0:02 sh В третьей строке все сказано. Из vi вы запустили shell с опцией -с для запуска языка shell . Это бесполезная трата целого shell! А если использовать указанный выше синтаксис или просто ":sh", то такая ситуация не возникнет. Поддержка Escape Кроме того, что редактор vi можно покинуть по Esc, он поддерживает некоторые другие возможности для выхода. Обладая различными возможностями выхода, инструментальные средства могут выполнять для Вас большую часть работы. Первый вариант синтаксиса - ":!cmd", который является префиксом для запуска любой команды вне редактора. В этом случае команда может быть любой командой раздела (1). Второй вариант синтаксиса - это ":!!". Это означает выйти (:!) и использовать последнюю командную строку как аргумент для запуска в новом языке shell. Например, если мы сказали: ":!ls", потом ":!!", :ls будет запущена опять. Второй ! ссылается ко всей предыдущей ко- мандной строке. Третий вариант синтаксиса - это ":!%". Это означает выйти (:!) и запустить команду, имя которой является именем этого файла (%). Когда вы нажимаете возврат каретки, % - замещается именем файла, что очень удобно при редактировании командных файлов. Вы можете сделать что-нибудь типа: $ vi tool . . . edit . . . :w Вы вызываете vi с именем файла, так что vi запоминает имя "tool" в своем буфере. Вы можете изменить что-то прямо тут в редакторе, записать изменения на диск, затем запустить новую копию файла. Редактор заполняет файл с именем "tool" и запускает его. Когда Вы выходите из исполняемого файла "tool", Вы попадаете назад в редактор, готовые внести изменения в текст и запустить программу снова. Одна из хороших последовательностей - это отредактировать файл, сделать изменения, записать их, запустить файл, используя %, внести изменения еще раз, перезапустить программу, напечатав :!!, что перезапускает последнюю команду escape, :!% . Таким образом цикл выходов и запусков программы образует три нажатия клавиатуры, :!!. Мы даже можем использовать эту возможность для компиляции программ на С. Если у нас есть командный файл, который называется "cg" (генератор компиляции) мы можем проще использовать vi: F = 'echo $ 1 | sed -e "s/^\(.*\).c$/\1"' cc $1 -o $F Потом мы можем выполнить последовательность такого типа: $ vi test.c ...edit... :!cg % или то же самое короче - 18 - : !cg test.c и заканчивается созданием исполняемого модуля "test". Макросы Другая возможность vi, которая поддерживает простой выход, - это механизм макросов. Хотя главным образом макросы нужны для того, чтобы можно было помещать команды редактирования в именованные регистры, которые часто используются. Таким образом вместо того, чтобы использовать синтаксис опять и опять, Вы просто используете макрос. Ниже приведен пример макроподстановки: i s/^[^ ]*/ [^ ]*/ "add @a Сначала нужно перейти в режим вставки, чтобы мы могли поместить команду в наш файл редактора. Мы печатаем команду подстановки и нажимаем ESC, чтобы закончить работу в режиме вставки. Команда подстановки говорит "В строках, которые начинаются с непустого символа, за которым следуют один или несколько символов такого же типа, поставить пробел перед непустой последовательностью символов". Далее мы печатаем "add", где "а" обозначает именованный регистр a и dd обозначает переместить строку в буфер. Теперь строка подстановки находится в буфере а. Чтобы ее выполнить мы просто напечатаем @a в командном режиме vi. Чтобы выйти мы можем выполнить ту же последовательность действий, но поместить команду типа :!ps -ef в редактор и переписать ее в буфер. Потом, когда мы говорим @a, мы входим в shell и запускаем команду ps. Команды такого типа можно помещать в именованные буферы от a-z. Последний способ использования макросов для поддержки выхода - это через команду map. Эта команда есть в ex и адресуется предшествующим двоеточием : из vi. Синтаксис для команды map выглядит так: :map lhs rhs Это устанавливает отображение левой стороны на правую сторону. Пример присвоения выглядит так: :map w :!who^M Теперь каждый раз, когда Вы печатаете w, будет выполняться действие выхода через ex, печататься команда who, потом печататься возврат каретки, который отправляет всю эту последовательность на выполнение. Все это по одному нажатию клавиатуры. Самое смешное начинается, когда Ваш терминал имеет функциональные клавиши. Vi обращается к функциональным клавишам по #0 -#9. Теперь мы можем зарезервировать функциональные клавиши для команд выхода. Простое присвоение будет: :map #1 :!ps -ef^ Каждый раз, когда Вы нажимаете функциональную клавишу F1, - 19 - запускается команда ps -ef. Команда "One-Liners" - крошечная, но мощная Следующий список - это подборка обычных команд, использованных таким образом, чтобы получить мощный эффект. Как мастер военного искусства, который убивает голыми руками, мастер UNIX часто может сложить вместе несколько обычных команд и достичь колоссального эффекта. Некоторые строки, которые будут представлены ниже, можно встретить в других местах этой книги, но они приводятся здесь повторно для облегчения поиска. Строки группируются в соответствии с командой, которая является ключевой в данной строке, однако, иногда сложно выделить такую команду как, например, в случае программного канала, когда Вы с двух сторон имеете важные команды. Заметьте, что некоторые из этих команд являются стандартными командами UNIX, в то время как другие - это командные файлы и программы, представленные ранее в этой книге. ACCTCOM * прочитать всю Вашу учетную информацию, начиная с последней команды. acctcom -b -u$LOGNAME * показать все учетные записи, запущенные с Вашего терминала и того, который запущен как суперпользователь acctcom -u# -l'tty' BANNER * напечатать сообщение на трех строках banner "line 1" "line2" "line3" * напечатать день недели и дату на одной строке, время на другой banner "'date|cut -d' ' -f1,3'" "'date|cut -d''-f4'" * послать сообщение на экран другого пользователя banner "Привет" "там" > /dev/tty01 BASENAME * очистить путь echo "Я за устройством 'basename\'tty\' ' '' BC * передать формулу на вход команде bc, которая должна выполнить умножение и присвоит результат PROD PROD = 'echo $NUM1 * $NUM2 | bc' CAT - 20 - * передать символы с клавиатуры в файл cat > file (печатать пока не встретится символ ^D для прекращения чтения) * получить ввод из конструкции "документ здесь" cat << -! Это образец текста, который печатается на экране ! CC * множественная компиляция в фоновом режиме из одной командной строки cc file1.c & cc file2 & cc file3.c & CD * перейти в каталаг, в котором находится файл cd 'имя директория\'путь файл\'' * перейти в каталог, который записан в переменной DESTINATION="/usr/bin" cd $DESTINATION * перейти в каталог, записанный в файле cd 'cat dest_file' CHMOD * включить бит исполнения файла chmod +x file * включить бит смены идентификатора пользователя и разрешить всем выполнение файла chmod 4755 file * установить бит sticky во включенное состояние chmod 1755 file CHOWN * установить себя владельцем файла chown $LOGMAME files * то же самое другим способом chown 'who am i| cut -d' '-f1' files * изменить право собственности для дерева - 21 - cd dest find . -print | sort | while read FILE do chown russ $FILE done CP * скопировать три уровня файлов в один вкаталог /tmp cp */*/* /tmp * то же самое другим способом cp 'find . -type f -print' /tmp * переключить пользовательское предложение cp -z CPIO * переместить дерево системы файлов в новое местоположение cd $SRC find .-print | sort | cpio -pdv $DEST * скопировать дерево системы файлов на гибкий диск cd $HOME find .-print | sort | cpio -ocBv > /dev/rfd0 * восстановить копию на стримере cd $DEST cpio -icBvt < /dev/rfd0 * выполнить "ls -l" на копии стримера cpio -icBvt < /dev/rfd0 CRON * запустите Ваш генератор сообщений о статусе каждый четверг в 6:00 a.m. 0 6 * * 4 /usr/russ/bin/status_msg * chmod на файл паролей * * * * * /bin/su root -c "chmod 777 /etc/passwd" CU * непосредственно обратиться последовательному порту на скорости 1200 бод cu -ltty00 dir * непосредственно обратиться к последовательному порту на скорости 9600 бод cu -ltty00 -s9600 dir * автоматически вызвать другую систему, используя комбинацию dial/modem cu -acua0 555-1212 - 22 - CUT * отсечь первое поле из файла passwd cut -d: -f1 /etc/passwd * отсечь имя из листинга команды who who | cut -d' ' -f1 who | awk '{print $1} DD * полная гибкая копия дорожка за дорожкой dd if = /dev/fd0 of=/dev/fd1 DOS * скопировать все файлы данного каталога на дискету DOS doscp * a: * скопировать все файлы с дискеты DOS в данный каталог dosls a: > /tmp/dosf for FILE in 'cat /tmp/dosf' do doscp a:$FILE done DU * выдать общее количество блоков для всех каталогов в / du -s /* * напечатать количество использованного места в каталоге каждого пользователя echo "total bytes: 'expr\'du -s $1\' \* 512'" ECHO * напечатать значение переменной shell'а echo $PATH $CDPATH * напечатать вывод вперемешку с обычным текстом echo " Мое имя $LOGNAME или 'logname' или 'who am i|cut -d' ' -f1'" * напечатать символы упраления в кавычках и без echo "\n\t Это записано в кавычках" echo \\n\\t Это записано без кавычек * напечатать и оставить курсор в конце той же строки - 23 - echo -n "prompt: " echo "prompt: \c" ED * запустить ed автоматичски с конструкцией "документ здесь" ed /etc/passwd <<-! 1,$p g/root/s//noroot w q ! EXPR * умножить два числа expr 512 \* 1024 * увеличить переменную на предопределенное значение x = 0; INC = 5 X='expr $X + $INC' FILE * найти все текстовые файлы file * | fgrep text * напечатать имена только текстовых файлов file * | fgrep text | cut -d: -f1 * more все текстовые файлы more 'file * | fgrep text | cut -d: -f1' FIND * найти все файлы в системе find / -print | sort * найти все файлы и распечатать список в формате long find / -exec ls -ld {} \; * напечатать имена всех регулярных файлов find / -type f print * найдите все каталоги и распечатайте содержимое find / -type d print | while read DIR do echo "listing $DIR" ls $DIR done * найдите все файлы, которые были модифицированы в последние 24 часа и распечатайте их список в формате long find / -atime -0 -exec ls -ld {} \; - 24 - * найдите все файлы setuid и setgid find / -perm -4000 -o -perm -2000 -exec ls -ld {} \; FINGER * укажите всех пользователей, вышедших из системы finger 'who | cut -d' ' -f1' * укажите всех пользователей в файле passwd cut -d: -f1 /etc/passwd | while read NAME do finger $NAME done GREP * найти случаи употребления шестнадцатиричных чисел в файле данных od -x datafile | grep 'A3F09' * найти свое имя в системе find / -type f print | while read FILE do grep "russ" $FILE /dev/null done HEAD * озаглавьте все текстовые файлы в текущем каталоге file * | fgrep text | cut -d: -f1 | while read FILE do echo "--------" echo "$FILE" echo "--------" head "$FILE" ID * определить, кто в данный момент является суперпользователем if [ "'id'" = "uid=0(root) gid=0(root)" ] then echo "you are root" fi * то же самое другим способом if id | fgrep root > /dev/null then echo "you are root" fi KILL * уничтожьте себя (выгрузите) kill -9 0 kill -9 $$ * завершите работу системы kill -1 1 * уничтожьте последний процесс, запущенный в фоновом режиме kill -9 $! - 25 - * уничтожьте процесс, идентификатор которого находится в файле kill -9 'cat idfile' LINE * взять строку с терминала LINE='line < /dev/tty' * взять строку из стандартного ввода cat datafile | while LINE = 'line' do echo $LINE done LOGIN * перейти из сгенерированной подсказки login ??? login: ^d login: * получить некоторую внутреннюю информацию (программа strings - это BSD) strings /bin/login | more LOGNAME * напечатать информацию о своем пароле grep '^'logname ':' /etc/passwd * получить информацию о своем процессе ps -fu 'logname' LS * выдать список скрытых файлов ls -ad .* * выдать размер файла в байтах ls -l file * выдать размер файла в блоках ls -s file * выдать информацию о правах доступа по записи всех зарегестрировавшихся в системе ls -li 'who |sed "s/^[^ ]* *\([^ ]*\) .*$/\/dev\/\1/p"' * получить помощь по испоьзованию команды ls -z * выдать список только каталогов ls -al |grep "^d" MAIL * послать почту всем пользователям - 26 - cut -d: -f1 |while read USER do echo "mailing to $USER" mail $USER done * послать почту из файла mail russ < /etc/passwd * послать почту из программного канала echo "Это текст почты" | mail russ MORE * напечатать все файлы текущего каталога more * * напечатать 10 строк за раз more -10 file cat file | more -10 MKDIR * опуститься на максимальную глубину while : do mkdir x cd x done * то же самое другим способом PATH="x" while : do mkdir $PATH PATH="$PATH/x" done NCHECK * найти все файлы, присоединенные к vi ls -li /bin/vi 40 -rwwx--x--t 1109344 Feb 14 1985 /bin/vi ncheck -i 40 /dev/root * найти все файлы установки идентификатора пользователя ncheck -s NM * посмотреть символьные таблицы всех nonstripped исполняемых файлов nm 'file *| grep "not stripped"|sed "s/^\(.*\):.*$/\1/" OD * посмотреть символы в именах файлов в текущем каталоге - 27 - od -c . * напечатать значение функциональных клавиш, комбинаций клавиш, и.т.д. od -cb (нажмите комбинацию клавиш) ^d (печатает строку) (нажмите что-нибудь еще) ^d (печатает следующую строку) ^d (выыходит из od) * сделать дамп копии на стримере od -c /dev/rfd0 * сделать дамп файловой системы od -c /dev/root PASSWD * как суперпользователь Вы можете установить в качестве пароля любую строку # passwd russ Changing password for russ (Изменение пароля для russ) Enter new password (minimum of 5 characters) (Введите новый пароль (минимум 5 символов)) Please use combination of upper, lowercase letters and numbers (Просьба использовать комбинации чисел и букв в верхнем и нижнем регистрах) New password: junk (Новый пароль: junk) Re-enter new password: junk (Новый пароль: junk) # * как обычный пользователь Вы должны будете вводить пароль с учетом количественных ограничений и ограничений по длине $ passwd russ Changing password for russ (Изменение пароля для russ) Enter new password (minimum of 5 characters) (Введите новый пароль (минимум 5 символов)) Please use combination of upper, lowercase letters and numbers (Просьба использовать комбинации чисел и букв в верхнем и нижнем регистрах) New password: junk (Новый пароль: junk) Too short. Password unchanged. (Слишком короткий. Пароль не изменен) $ PR * вывести многоколоночный список имен файлов ls $@ | pr -5t * напечатать файлы из списка - 28 - pr 'find . -name "*.c" -print | sort' PS * напечатать полную информацию обо всех активных процессах ps -aef * напечатать информацию обо всех процессах, управляемых Вашим терминалом ps -f * напечатать информацию о процессах, связанных с терминалом tty00 ps -ft00 * напечатать информацию о процессах, связанных с пользователем russ ps -furuss * BSD синтаксис для печати всех процессов ps -aux * BSD синтаксис для печати всех процессов, связанных с терминальным устройством ps -xut00 PWD * сохранить текущий рабочий каталог PWD='pwd' * вернуться в ранее сохраненный рабочий каталог cd $PWD RM * удалить все файлы, кроме каталогов с файлами rm * * удалить пустые каталоги rmdir dirs * удалить каталоги, имеющие файлы rm -r dirs * удалить все файлы в режиме, когда система не будет задавать никаких вопросов rm -rf * * удалить каждый файл в системе по отдельности rm -rf / SH * прочитать список поэлементно for ELEMENT in 'cat /etc/motd' do - 29 - echo $ELEMENT done * прочитать список построчно cat /etc/motd | while read LINE do echo $LINE done * цикл пока - навсегда (while-forever) while : do echo $PS1 read CMD case $CMD in "") break;; esac done * управляемый цикл while read CMD while [ "$CMD" != ""] do case $CMD in user-cmd) do_it;; esac echo $PS1 read CMD done * переполнениие тестового стека при обработке прерывания trap "echo trapping; kill $$" 2 3 15 * выгрузка из языка shell несколькими способами exit eof character (usually control -d) kill -9 0 kill -9 $$ STTY * посмотрите все свои установки stty -a * посмотрите терминальные установки другого терминала stty -a < /dev/tty01 * установить передачу бод на другую скорость для другого терминала stty 300 < /dev/tty01 * динамически установить control-A как клавишу прерывания stty intr ^a * включить эхо-сопровождение терминала stty -echo - 30 - SU * тестовый цикл для уничтожения легких паролей awk '{FS =":"; print $1,$5} '/etc/passwd|while read N C do echo "\n$N\t$C" su $N done TAIL * проследить в реальном времени запись транзакций файла входа в систему (logfile) uucp tail -f /usr/spool/uucp/LOGFILE * посмотреть последнюю строку файла tail -1 file * посмотреть последние 10 символов переменной echo "$VAR" | tail -10c TAR * сделать копии файлов в Вашем home-каталоге не разрывая файл, но формируя копии на куски по 1200 блоков cd tar cvefbk /dev/rfd0 10 1200 . * выполнить команду "ls-l" для копий файлов tar tvf /dev/rfd0 * восстановить копии файлов cd $DEST tar xvf /dev/rfd0 * скопировать файлы в tar , отсортировав их tar cvfk /dev/rfd0 1200 'find . -print | sort' TEE * отправьте свой вывод на экран другого терминала sh | tee /dev/tty01 * захватите вывод других команд fsck /dev/root | tee capture cu -ltty00 dir | tee capture TEST * проверьте эквивалентность двух строк test "$S1" = "$S2" * проверьте эквивалентность двух чисел test "$N1" -eq "$N2" * то же самое другим способом (заметьте что /bin/[присоединен к /bin/test) - 31 - [ "$S1" = "$S2" ] [ "$N1" -eq "$N2" ] TOUCH * сделайте текущим доступ и измените время всех файлов в Вашем home каталоге find $HOME -exec touch {} \; find $HOME -print | while read FILE do touch $FILE done TTY * показать права доступа по записи на Вашем терминале ls -l 'tty' * включить и отключить доступ пользователей к Вашему терминалу chmod 666 'tty' chmod 600 'tty' UUCP * скопировать имена всех файлов в файл в общедоступный каталог на другой системе for FILE in 'cat datafile' do echo "копирование $FILE" uucp $FILE sys! ~/user done * поставить файл в очередь, не инициировать вызов, взять файл из Вашего первоначального каталога, не копирровать его в spool каталог. uucp -r -c file sys!/tmp VI * выполните текущую строку как команду языка shell :.w !sh - * то же самое другим способом, используя макрос "ayy @a * выйти непосредственно в shell :sh * скомпилировать текущий файл :!sh * запустить имя текущего файла как комаду языка shell :!cc % * запустить еще раз последнюю команду :!% - 32 - * запустить команду и поместить ввывод на текущую строку (переписать) :.!who am i * запустить команду и поместить ввывод на новую строку :.r !who am i :r !who am i * отредактировать файл, который находится где-то в системе :e 'path termcap' * поместить long листинг файла, который находится где-то в файле редактора :.!ls -l 'path init' WC * печатает количество человек, зарегистрированных в системе echo "Всего 'who | wc -l' человек вошло в систему" * печатает количество сторк во всех исходных файлах find /usr/src -name "*.c" -exec cat {} \; | wc -l WHO * печатает количество и имена зарегистрированных пользователей who | awk '{ print "user:",$1,"\tdevice:",$2 cnt = cnt + 1 } END { print cnt,"пользователи, вышедшие из системы"}' * печатает who | while read NAME TTY TIME do echo "пользователь: $NAME tty: $TTY time: $TIME done

    * ПРИЛОЖЕНИЕ 1. Переменные среды выполнения *

Некоторые переменные среды являются зарезервированными словами, которые использует интерпретатор shell. Их значения устанавливаются, когда вы регистрируетесь в системе. Другие переменные используются различными другими частями системы UNIX. Имея под рукой полный спи- сок, вы можете убедиться, что все необходимые переменные определены. Имена переменных могут варьироваться в System V, BSD и XENIX. Здесь приведены имена для System V. Эти shell-переменные являются частью среды вашего регистрацион- ного интерпретатора shell. Они хранятся в виде символьных строк, и если уж они установлены, от них нельзя избавиться. Только переуста- новка их значений на пустые строки может исключить их присутствие. Когда меняются значения переменных, новые значения локальны для теку- щего интерпретатора shell до тех пор, пока они не будут экспортирова- ны. CDPATH Строка поиска, определяющая, куда вы можете перейти с помощью команды cd HOME Место хранения ваших файлов в системном дереве IFS Внутренний разделитель полей, который определяет символы, разделяющие слова между собой LOGNAME Ваше регистрационное имя, определенное в /etc/passwd MAIL Определяет местонахождение вашего почтового ящика, чтобы вас можно было оповестить о поступлении вам новой почты PATH Определяет порядок и имена каталогов для поиска исполняемых модулей PS1 Главная строка подсказки, которая отображается в качестве вашего обычного приглашения PS2 Вторичная строка подсказки, которая отображается, когда интерпретатору shell нужен дополнительный ввод TERM Строка, определяющая ваш тип терминала TERMCAP Устанавливается либо на файл описания терминала, либо на запись этого файла TZ Зона времени, понимаемая так, как описано в ctime(3) SHELL Имя shell-интерпретатора, который вы запускаете EXINIT Строка инициализации для редактора ex

    ПРИЛОЖЕНИЕ 2

ПОСЛЕДОВАТЕЛЬНОСТЬ ДЕЙСТВИЙ ИНТЕРПРЕТАТОРА SHELL Shell выполняет определенную последовательность действий по ин- терпретации команд для каждой прочитанной им строки. Зная эту после- довательность, вы можете изолировать многие проблемы на соответствую- щем уровне. Иногда на определенном шаге этой последовательности могут появиться странные вхождения символов. Поэтому важно знать, когда из- меняются значения переменных. Один из примеров относится к раскрытию имени файла и символу звездочки. Shell НЕ раскрывает метасимволы во время присвоения значе- ний. Оператор F=* означает в действительности, что переменной F прис- ваивается один символ. Когда выполняется шаг 7 последовательности действий, звездочка раскрывается как метасимвол имени файла, превра- щаясь во все имена файлов в текущем каталоге. Это можно продемонстри- ровать на примере команды "echo $F". Для того чтобы сохранить лите- ральное значение звездочки, вам нужно экранировать ее, что защитит ее от шага 7. Получается команда echo "$F". А для того чтобы вообще по- давить значение переменной F, вам следует исключить шаг 3 - подста- новку параметров. Указывая команду echo '$F', вы печатаете буквы $F, а не значение переменной F. Действия выполняются в таком порядке: 1. Чтение входа и синтаксический разбор 2. Словесное сопровождение разбора 3. Подстановка параметров 4. Подстановка команд 5. Переназначение ввода-вывода 6. Обработка внутренних разделителей полей 7. Раскрытие имени файла 8. Трассировка выполнения 9. Обработка переменных среды 10. Выполнение команды Далее эти шаги рассматриваются более подробно. Когда вы програм- мируете на языке shell, попытайтесь представить себе, какой шаг вы используете для каждой строки текста программы. Возможно, вы путаете два-три шага между собой. Зная порядок шагов, вы можете проще опреде- лять эффект от действия ваших команд и сокращать число возможных оши- бок. 1. Командная строка или логическая конструкция читается с терми- нала или из файла данных. Чтение останавливается при обнаружении сле- дующих символов: точки с запятой (;), символа фоновой задачи (&), ло- гического и (&&), логического или (||) либо символа новой строки (\n). Выполняется разбор введенных данных на слова с учетом пробелов и табуляций. 2. Если в текущем интерпретаторе shell установлен флаг многос- ловности (-v), то прочитанная строка отображается на стандартное уст- ройство регистрации ошибок. 3. Выполняется подстановка параметров. Сюда входит подстановка позиционных параметров, подстановка переменных и подстановка специ- альных выражений. Параметры всегда имеют префикс в виде денежного знака ($). 4. Выполняется подстановка команд. Это относится ко всем коман- дам, взятым в символы ударения (`). Такая команда вычисляется и вы- полняется, а результирующий текст заменяет исходное выражение в пол- ной командной строке. Выполняемая команда может содержать последовательные команды, конвейеры или команды, сгруппированные в скобках. Любые лишние пробелы, табуляции или символы новой строки, появившиеся в результате выполнения команды, впоследствии удаляются при обработке внутренних разделителей полей. Если нужно сохранить эти лишние символы, примените двойные кавычки вокруг всего выражения. 5. Проверяется переназначение ввода-вывода. Если таковое имеет- ся, исходный дескриптор файла (0, 1 или 2) закрывается, а затем отк- рывается повторно с новым значением. Вновь открытый дескриптор файла занимает в файловом вводе-выводе то же место, что и закрытый файловый дескриптор. Символы переназначения удаляются из командной строки. 6. Поскольку командная строка могла измениться по сравнению со своим исходным состоянием в результате подстановок, она вновь разби- рается на слова с учетом переменной среды IFS. Эта переменная содер- жит разделители между полями, которые отделяют слова друг от друга в командной строке. Каждый символ командной строки, который занесен в IFS, заменяется на пробел, чтобы разграничить слова. Все неэкраниро- ванные пробелы, табуляции, символы новой строки и нулевые аргументы удаляются из командной строки. Все экранированные значения переменных защищены от разбора, выполняемого в соответствии с IFS. Для того что- бы увидеть значение IFS, введите такой конвейер команд: echo "<$IFS>" | od -bc 7. Далее shell ищет все слова, для которых требуется раскрытие имени файла (метасимвола). Делается попытка сопоставить образец с файлами текущего каталога. Если подходящие файлы найдены, они заменя- ют выражение в командной строке. Если соответствия не обнаружено, ме- тасимволы остаются в этом выражении. Все присвоения переменным защи- щены от раскрытия метасимволов. Типичным примером является команда "ls z*". Если имена каких-либо файлов начинаются с буквы z, эти имена перечисляются. В противном случае печатается сообщение "z* not found". 8. Если установлен флаг трассировки выполнения (-x), то команд- ная строка отображается на стандартное устройство регистрации ошибок перед тем, как она будет фактически выполнена. Если это командная строка, то она выводится с префиксом "+", если же это просто присвое- ние значения переменной, префикса нет. 9. На этом шаге всем переменным присваиваются значения, затем в соответствии с переменной среды PATH ищется местонахождение команды. Присвоение значений переменным происходит справа налево вдоль команд- ной строки. Поиск по переменной PATH, наоборот, происходит слева нап- раво. Если имя найдено, то полное маршрутное имя заменяет вызов ко- манды в командной строке. Если переменная PATH имеет пустое значение, подразумевается текущий каталог. Если в каком-либо месте исходного имени команды имеется косая черта (/), то переменная PATH не просмат- ривается, а считается, что указано полное маршрутное имя. 10. Наконец, команда выполняется. Если это встроенная команда, текущий shell отрабатывает ее. В противном случае делается попытка загрузить команду в память, как если бы это была скомпилированная программа. Если эта попытка успешна, команда выполняется посредством системного вызова exec(2). Если загрузка команды не удалась, то счи- тается, что это командный файл еще одного интерпретатора shell, и по- рождаемый shell читает этот командный файл в качестве своих данных.

    ПРИЛОЖЕНИЕ 3

СПЕЦИАЛЬНЫЕ СИМВОЛЫ SHELL-ПЕРЕМЕННЫХ Приведенные здесь символы рассматриваются shell-переменными как специальные, так как они означают завершение слова. Для того чтобы использовать символ в его обычном значении, а не в качестве специаль- ной функции, нужно экранировать его обратной косой чертой (\) или взять в одинарные кавычки. \b Пробел: код 20 (шестнадцатеричный), ограничитель слов \n Символ новой строки: ^j, код A (шестнадцатеричный), ограничитель слов \t Табуляция: ^i, код 9, ограничитель слов ; Точка с запятой: завершает программный конвейер ( Левая скобка: ограничивает подчиненный shell ) Правая скобка: ограничивает подчиненный shell | Вертикальная черта, или символ программного конвейера: разделяет команды ^ Стрелка вверх, знак вставки: старый символ, используемый в качестве | > Правая угловая скобка (знак больше): переназначает стандартный вывод < Левая угловая скобка (знак меньше): переназначает стандартный ввод & Амперсанд: вызывает асинхронное (фоновое) выполнение { Левая фигурная скобка: очерчивает слово для первоначального разбора слова } Правая фигурная скобка: завершает знак очерчивания слова

    ПРИЛОЖЕНИЕ 4

СПЕЦИАЛЬНЫЕ СИМВОЛЫ SHELL-ОПЕРАТОРОВ Эти символы встречаются в синтаксисе операторов языка shell. Их следует рассматривать как зарезервированные. Отдельные символы могут использоваться по-разному. Например, символ # является комментарием в операторе, а также может быть параметром, как в записи $#, означающей количество аргументов в командной строке. && Двойной амперсанд: выполнить список, если программный конвейер отработал успешно || Двойная вертикальная черта: выполнить список в случае неудачи программного конвейера ` Знак ударения: перехватить стандартный вывод в команде * При использовании в качестве параметра соответствует всем позиционным параметрам; является также символом генерации имен файлов, соответствующим любой строке # Комментарий до конца строки; соответствует также количеству позиционных параметров в командной строке ? При использовании в качестве параметра соответствует статусу завершения последней синхронно выполненной команды; используется также при генерации имен файлов и означает при этом любой одинарный символ - Обозначает флаги, влияющие на функционирование интерпретатора shell $ Вводит заменяемые параметры; соответствует также идентификатору процесса ! При использовании в качестве параметра соответствует идентификатору процесса последнего фонового задания; применяется также в команде проверки, где означает "не" " Двойная кавычка: окаймляет символы и разрешает производить подстановку параметров ' Одинарная кавычка: окаймляет символы, но запрещает подстановку параметров \ Обратная наклонная черта: экранирует одиночный символ, чтобы снять его специальное значение [] Альтернативное использование для вызова команды проверки. Применяется также при генерации имен файлов, означая при этом диапазон символов @ Соответствует каждому позиционному параметру командной строки >> Дополнить стандартный вывод << Переназначить стандартный ввод на вводимые строки текста & Используется как символ фонового процесса; соответствует также "файловому дескриптору", если используется в переадресации

    ПРИЛОЖЕНИЕ 5

ВСТРОЕННЫЕ ОПЕРАТОРЫ ЯЗЫКА SHELL Перечисленные ниже команды употребляются в программах на языке shell. Здесь имеется в виду System V Bourne shell. В System V.2 - более новой версии System V - интерпретатор shell имеет некоторые дополнительные команды, перечисленные в отдельном списке. Имеется также интерпретатор ksh (фактически это улучшенный Bourne shell), обладающий возможностями командных строк, характерными для csh (Си-shell). Интерпретатор ksh здесь не рассматривается. ПРИМЕЧАНИЕ. Не позволяйте команде exec сбить вас с толку. Это не то же самое, что команда exec(2). Данная команда exec обрабатывается интерпретаторм shell, а exec(2) является системным вызовом. КОМАНДЫ System V Bourne Shell . Точка: запустить данную команду из текущего интерпретатора shell, а не из порождаемого : Двоеточие: ничего не делать, а только возвратить успешный статус (0) {} Фигурные скобки: запустить последовательный список команд break Оборвать следующую итерацию текущего цикла case Многократный выбор if-then-else cd Сменить каталог continue Перейти на следующую итерацию цикла for, while или until eval Выполнить еще раз этап подстановки переменных exec Выполнить команду с аргументами, перекрывая текущий shell exit Остановить выполнение текущего командного файла export Отправить значение переменной всем подчиненным интерпретаторам shell for Управляющее слово в цикле for-do-done if Управляющее слово в последовательности if-then-else newgrp Изменить текущий идентификатор группы read Одну строку стандартного ввода присвоить переменной в качестве значения readonly Объявить переменную только для чтения, ее значение изменять нельзя set Включение и выключение флагов конфигурации shell shift Убрать позиционный параметр из командной строки test Вычислить взаимосвязи между строками и целыми числами times Печатать время работы процессов, запущенных из shell trap Определить обработчики прерываний для конкретных сигналов ulimit Установить предел размера файлов в 512-байтных блоках umask Маска прав доступа к файлам, используемая при их создании until Управляющее слово в цикле until-do-done wait Shell ждет завершения указанных порожденных процессов while Управляющее слово в цикле while-do-done ДОПОЛНИТЕЛЬНЫЕ КОМАНДЫ System V.2 hash При поиске команд использовать хэширование name Определить имя shell-функции pwd Сообщить текущий каталог; теперь это встроенная команда для ускорения return Выйти из shell-функции и возвратить значение set -f Запретить фазу генерации имен файлов set -h Сохранить, а не выполнять функциональные команды, если они определены type Определить, каким образом можно интерпретировать имя в качестве команды unset Удалить shell-переменные и функции

Last-modified: Thu, 26 Mar 1998 10:32:29 GMT