Обложка 1
Титульный
Аннотация
Предисловие к русскому изданию
Предисловие авторов
Глава 1. Первые шаги
Глава 2. Файлы и простые команды
Язык программирования shell
Глава 3. Редактор текстов ОС UNIX
Глава 4. Язык программирования Си
Глава 5. Файловая система ОС UNIX
Глава 6. Инструментальные средства ОС UNIX
Глава 7. Подготовка документации в ОС UNIX
Глава 8. Процессы и системные вызовы
Глава 9. Библиотеки ОС UNIX
Глава 10. Сопровождение ОС UNIX
Приложение А
Приложение Б
Приложение В
Приложение Г
Приложение Д
Приложение Е
Ответы к упражнениям
Список литературы
Содержание
Выходные данные
Обложка 2
Текст
                    Введение в ОС (УШкПЗС
БАНАХАМ М. РАТТЕР Э.
Введение
в операционную
систему

Введение в операционную систему UNIX
UNIX — the Book M.F. Banahan, B. Tech., M.Sc. and A. Rutter, 8. Tech., M.Sc. Sigma Technical Press
М. БАНАХАМ Э. РАТТЕР Введение в операционную систему им ох Перевод с английского В. В. Леонаса Под редакцией И. Г. Шестакова МОСКВА "РАДИО И СВЯЗЬ" 1986 Sciin AAW
Б БК 32.973 Б 23 УДК 519.68 Редакция переводной литературы Банахам М., Раттер 3. Б 23 Введение в операционную систему UNIX: Пер. с англ. — М.: Радио и связь, 1986. — 344 с., ил. 8 nep.: 1 р. 90 к. 15000 экз. В книге английских авторов дан обзор основных средств мобильной операционной системы UNIX, применяемой на многих ЭВМ различной производительности, включая PDP-l 1, ЭВМ фирм IBM, Hewlett-Packard, а также микро-ЭВМ на базе различных микропроцессоров. Описаны ос- новные команды ОС UNIX, структура файловой системы, язык програм- мирования Си, служебные программы, в том числе средства работы с текстами. В приложениях приведены справочные данные о системе. Ма- териал книги рассчитан на практическое использование в процессе осво- ения операционной системы UNIX и ей подобных. Для программистов. 2405000000-043 ББК 32.973 D I 046(011-86 ©1982, M.F.Banahan and A Rutter © Перевод на русский язык, предисловие к русскому изда- нию, примечания редактора и переводчика. Издательство “Радио и связь", 1986
ПРЕДИСЛОВИЕ К РУССКОМУ ИЗДАНИЮ В последнее время во всем мире наблюдается повышенный интерес к операционной системе UNIX (ОС UNIX) и подобным ей операционным сис- темам, число которых быстро растет. Повышенный интерес к этой операцион- ной системе (а правильнее было бы сказать — к этому семейству операцион- ных систем) обусловлен в основном двумя причинами. Во-первых, ОС UNIX реализована практически полностью на языке про- граммирования Си, являющемся языком программирования высокого уровня, и потому считается мобильной операционной системой; версии ОС UNIX реализованы для большого числа типов ЭВМ, относящихся к различ- ным классам: от 8-разрядных микро-ЭВМ Intel-8080 до супер-ЭВМ Сгау-1. Стремительное развитие современной вычислительной техники сделало воп- рос о мобильности программного обеспечения ЭВМ вопросом первостепен- ной важности. Частая смена оборудования ставит программистов перед пе- чальной необходимостью переписывать накопленное немобильное програм- мное обеспечение. Что же касается мобильной операционной системы, то помимо указанного достоинства она имеет еще одно: независимость интер- фейса пользователя от архитектуры используемой ЭВМ. Во-вторых, ОС UNIX обеспечивает чрезвычайно простой интерфейс с поль- зователем и предоставляет последнему большой набор инструментальных средств. Это обстоятельство также соответствует современным требовани- ям к программному обеспечению ЭВМ. Действительно, все чаще за клавиату- рой терминала ЭВМ можно увидеть людей, интересы которых весьма далеки от программирования, а подготовка оставляет желать лучшего. Комбинируя нужным образом различные инструментальные средства, входящие в состав стандартного набора инструментальных средств ОС UN IX,такой "неквалифи- цированный" пользователь сможет решать весьма сложные задачи, вовсе не прибегая к программированию. Эти, а также ряд других особенностей ОС UNIX явились причиной того, что эта операционная система фактически стала стандартной для 16-разряд- ных микро-ЭВМ. Более того, этот стандарт был принят официально для 16- и 32-разрядных микро-ЭВМ, поскольку в 1983 г. четыре ведущие американ- ские фирмы-изготовители полупроводниковых изделий Intel, Motorola, Zilog и National Semiconductor приняли решение о постановке System V (новейшей версии ОС UNIX) в качестве стандартной на свои 16- и 32-раз- рядные ЭВМ. Разработка ОС UNIX в фирме Bell Laboratories началась в 1969 г. (назва- ние было предложено Б.У. Керниганом в 1970 г.), когда ее однопользова- 5
тельская версия, написанная на языке ассемблера, была реализована для минЬ-ЭВМ PDP-7 и PDP-9 •(предшественниц широко известного семейства мини-ЭВМ PDP-11). В 1971 г. ОС UNIX была перенесена на мини-ЭВМ PDP-11/2O. Версия ОС UNIX, написанная на языке программирования Си, впервые появилась в 1973 г. В мае 1975 г. была выпущена версия ОС UNIX, известная сегодня как версия 6, а в 1979 г. в результате работ по переносу этой операционной системы на ЭВМ Interdata 8/32 появилась версия ОС UNIX, известная сегодня как версия 7. Последующие версии ОС UNIX получили названия System III, System IV и System V. Говоря о различных версиях ОС UNIX, нельзя не упомянуть о версиях, разработанных в Кали- форнийском университете (г. Беркли) для ЭВМ семейства VAX-11 и называ- емых сокращенно UNIX/BSD (Berkley System Distribution); последней из-этих версий является UNIX/BSD 4.2. В СССР за последние годы также можно наблюдать рост интереса как к самой ОС UNIX, так и к положенным в основу этой операционной системы концепциям. В 1983 г. институтом электронных управляющих машин была выпущена предварительная версия ИНМОС (инструментальной мобильной операционной системы), которая предназначена для эксплуатации на ми- ни-ЭВМ СМ-4, СМ-1420, СМ-1644, "Электроника-100/25" и является про- граммно совместимой с версией 6 ОС UNIX. В настоящее время готовится к выпуску версия ИНМОС, которая будет программно совместима с вер- сией 7 ОС UNIX. Рост интереса к ОС UNIX в СССР и появление программно совместимой с ней операционной системы для массово производимых оте- чественных мини-ЭВМ стимулировали подготовку различными издательства- ми СССР издания целого ряда оригинальных и переводных книг по этой тематике. Предлагаемая вниманию читателей книга является одной из пер- вых в этом ряду. Изданная на английском языке в 1982 г. книга М. Банахана и А. Раттера предназначена в первую очередь для тех, кто не имеет предварительной под- готовки и лишен опыта программирования и использования ЭВМ, но по тем или иным причинам хочет научиться использовать ОС UNIX для решения за- дач прикладного характера. Авторы поставили перед собой трудную задачу, пытаясь совместить в рамках одной относительно небольшой книги учеб- ник и справочное руководство по ОС UNIX, и попытка их может быть рас- ценена как вполне успешная. Причиной, побудившей авторов к созданию такой необычной бифункциональной книги, является то обстоятельство, что документация по ОС UNIX, предназначенная для использования в ка- честве справочного материала, с одной стороны, имеет слишком большой объем, а с другой стороны, часто оказывается непонятной для неподготов- ленного пользователя. Кроме того, подобное разделение "идеи" и "набора фактов" представляется нам единственно возможным способом уместить на страницах сравнительно небольшой книги гигантский по объему материал. Действительно, все десять глав книги посвящаются авторами описанию "идеи" — принципов организации или функционирования ОС UNIX и ее 6
компонент. При этом авторы снабдили текст множеством тщательно подо- бранных примеров, одним из основных качеств которых является их инва- риантность относительно множества существующих на сегодняшний день версий ОС UNIX. В то же время приложения А — Е содержат чрезвычайно сжатое изложение "набора фактов" — здесь перечислены команды ОС UNIX вместе с их флагами, системные вызовы, инструментальные средства и даже стандартные библиотечные функции ОС UNIX. Авторы уделили боль- шое внимание вопросам методологии изложения материала. Учитывая ори- ентацию книги на тех, кто не имеет предварительной специальной подготов- ки, авторы использовали подход "от частного — к общему", названный ими "сначала практика, а затем теория". Хотя такой подход в значительной мере облегчает восприятие материала неподготовленным читателем, тем не менее он также имеет свои недостатки, что проявляется в тех случаях, когда не- обходимо ввести некоторое достаточно общее понятие. Книга написана лег- ким, иногда даже чересчур легким языком, в результате чего не всегда обес- печивается достаточная строгость изложения материала, хотя сточки зрения неподготовленного читателя, на которого рассчитана эта книга, наглядность и ассоциативность изложения, быть может, важнее строгости вводимых определений. Обилие сносок в книге в значительной мере обусловлено на- личием в ней ряда мест, допускающих неоднозначное толкование, появле- ние которых является совокупным результатом использования подхода "от частного — к общему" и излишне облегченного языка. Указанные недостатки не снижают в целом благоприятного впечатления от книги, полезность и своевременность издания которой на русском язы- ке не вызывают сомнений. В заключение мы хотели бы выразить надежду, что читатели получат поль- зу от знакомства как с этой книгой, так и с операционной системой UNIX. И. Г. Шестаков В. В. Леон ас
ПРЕДИСЛОВИЕ АВТОРОВ Эта книга посвящена описанию операционной системы UNIX1 (ОС UNIX) и подобных ей операционных систем. Операционная система UNIX была разработана в начале 1970-х годов в фирме Bell Laboratories (США). Ее авторы, уставшие от борьбы с громозд- кими и неуклюжими операционными системами, существовавшими в то время, попытались создать операционную систему, использование которой приносило бы радость. Сегодня уже можно сказать, что их попытка увенча- лась успехом. Различные версии ОС UNIX впервые начали эксплуатировать в коллед- жах и университетах, где они быстро завоевали популярность, а в ряде мест вытеснили другие операционные системы. С появлением в 1978 г. версии 7 ОС UNIX, а в 1982 г. - System III2 началась эра широкого распространения этой операционной системы для неакадемических приложений. Такая большая популярность ОС UNIX обусловлена двумя причинами. Первой причиной является мобильность ОС UNIX и то, что последняя уже эксплуатируется на множестве ЭВМ различных типов. Написав програм- му на некотором языке программирования высокого уровня (например, на Коболе, Фортране 77 или на Си — языке программирования, на котором на- писана сама ОС UNIX) и отладив эту программу на некоторой ЭВМ, работаю- щей под управлением ОС UNIX, можно быть уверенным, что эта программа без каких-либо изменений будет правильно выполняться на любой другой ЭВМ, работающей под управлением ОС UNIX. Следовательно, "операцион- ное окружение" программы не зависит от используемой аппаратуры. Это — весомый аргумент в пользу стандартной операционной системы. С точки зрения пользователя это идеально — он должен научиться как следует рабо- тать с одной-единственной операционной системой и забыть о существовании других. Не тратить долгие месяцы на подробное изучение новой операцион- ной системы каждый раз, когда возникает необходимость перехода к ис- пользованию ЭВМ другого типа — мечта каждого, кто хоть раз проделал это. Вторая причина популярности ОС UNIX — это ее удобство. Сразу же после своего появления ОС UNIX, еще не став по-настоящему коммерческой Произносится ЮНИКС. (Прим, пер.) Система III — это название одной из версий ОС UNIX. (Прим, ред.) 8
операционной системой, не обладая преимуществами, связанными с мобиль- ностью, так как она была реализована только для ЭВМ типа PDP-11, завоева- ла популярность в кругах академических пользователей. Эта операционная система представляет собой первую простую, модульную, гибкую и хорошо структурированную операционную систему для мини-ЭВМ. Пользователям ОС UNIX предоставляется прекрасный набор инструментальных средств, комбинируя отдельные элементы которого они могут решать весьма слож- ные задачи, не прибегая к написанию программ. В состав стандартного на- бора инструментальных средств ОС UNIX входят элементы различной слож- ности — от простой утилиты, обеспечивающей подсчет числа слов в файле, до "компилятора компиляторов", который по формальной грамматике не- которого языка программирования создает практически половину компи- лятора с этого языка программирования. Эти инструментальные средства в совокупности с файловой системой, системой ввода-вывода и чрезвычайно мощным командным языком, представляющим собой в действительности язык программирования высокого уровня, обеспечивают удобство исполь- зования ОС UNIX и ее высокую эффективность при решении задач разработ- ки программного обеспечения. Данная книга написана, с одной стороны, для тех, кто приступает к ис- пользованию ОС UNIX, а с другой стороны, для тех, кто просто хочет озна- комиться с возможностями этой операционной системы и принципами ее работы. Первая половина книги адресована начинающим пользователям. В ней описываются, как мы надеемся, достаточно полно, в простой и доступ- ной форме, процедура входа в ОС UNIX, использование командного языка и редактора текстов, основы программирования на языке программирова- ния Си и применение инструментальных средств. Вторая половина книги предназначена для более опытных пользователей. В ней описывается функ- ционирование ОС UNIX. Кроме того, значительную часть второй половины книги составляют материалы справочного характера. Эти материалы вклю- чены в книгу на основе нашего опыта работы с ОС UNIX, показавшего удобство их применения особенно в тех случаях, когда под рукой нет эк- земпляра фирменных руководств по использованию ОС UNIX. Мы ни в коем случае не претендуем на то, что наша книга заменит сущест- вующие фирменные руководства по использованию ОС UNIX. Это только попытка изложить максимум необходимой, с нашей точки зрения, пользо- вателю информации в достаточно кратком и удобочитаемом виде. Мы были крайне осторожны в выборе примеров для иллюстрации излагаемого мате- риала. Везде, где это было возможно, нами была сделана попытка дать под- робное объяснение тех вопросов, которые, по нашему мнению, изложены в фирменных руководствах по использованию ОС UNIX не слишком удачно. С одной стороны, мы попытались дополнить "официальную" документацию по ОС UNIX, а с другой стороны — создать краткий справочник по ОС UNIX для начинающих пользователей, предполагающих в дальнейшем активно применять эту операционную систему. 9
Работая над книгой, мы ориентировались в качестве стандарта на версию 7 ОС UNIX, разработанную фирмой Bell Laboratories, Среди имеющихся в настоящее время версий ОС UNIX и подобных ей операционных систем толь- ко небольшое их число отличается сколько-нибудь серьезно от этого стан- дарта. Если Вы собираетесь приобрести ОС UNIX или подобную ей операци- онную систему и Вас беспокоят вопросы совместимости с другими верси- ями, то рекомендуем обратиться за консультацией в Европейскую группу пользователей ОС UNIX (European UNIX User Group). Надеемся, что эта книга поможет Вам получить от использования ОС UNIX столько же радости, сколько получили от ее использования мы сами. Майк Банахан, Энди Раттер Бредфордский университет, 1982 г.
ГЛАВА 1. ПЕРВЫЕ ШАГИ 1.1. ВВЕДЕНИЕ Данная глава является первой из пяти вводных глав, представляющих собой подробное руководство по практическому использованию ОС UNIX. В некоторых случаях в этих главах мы были вынуждены упоминать о сред* ствах, подробное описание которых приводится позднее. Это является след- ствием подхода "сначала практика, а затем теория", который, с нашей точки зрения, является оптимальным при изучении ОС UNIX. Некоторые пользо- ватели были бы рады возможности не задумываться над тем, как и почему именно так работают те или иные применяемые ими средства, поскольку у них и без этого хватает забот. Однако, не зная, как работает то или иное средство, можно, используя его, иногда получить весьма неожиданные ре- зультаты. Поэтому мы старались приводить в каждом параграфе по возмож- ности наиболее полные объяснения. Каждый параграф основан на сведениях, изложенных в предыдущих па- раграфах, при этом предполагается полное понимание предыдущего матери- ала.' Поэтому Вы можете встретиться с трудностями, если ограничитесь беглым просмотром первых глав вместо подробного изучения излагаемого в них материала. Мы рекомендуем следующий порядок чтения книги: 1. Читайте отдельные главы книги последовательно, в соответствии сих порядковыми номерами. 2. Прочитав главу, проделайте предлагаемые упражнения. 3. Сядьте за терминал и начинайте экспериментировать. Проведение экспериментов является хорошим способом обучения, по- скольку заставляет Вас интерпретировать полученные результаты. В этом случае Вам скорее всего потребуются консультации коллеги, обладающего большим, чем Вы сами,опытом. Обращаясь за консультацией, Вы должны быть уверены в том, что тот, к кому Вы обращаетесь, в состоянии дать тре- буемую консультацию. Тщательно выбирайте гуру1. Ищите более опытного коллегу, говорящего тихим голосом, обладающего живым выражением лица и ясным взором. Если тот, кого Вы выбрали, печатает всеми десятью паль- цами, то это хороший признак. Ни в коем случае не обращайтесь за помощью 1 Гуру — наставник, учитель {санскр.), (Прим, пер.) 11
к одержимым программистам1. Отличительными признаками одержимого программиста являются мешки под глазами, застывшее выражение лица и остекленевший взгляд, давно немытое и небритое лицо, всклокоченные во- лосы. Лица этого типа считают, что они знают все и без устали заявляют об этом каждому встречному. Даже если одержимый программист действитель- но обладает широкими и глубокими познаниями по интересующему Вас вопросу, то, как правило, он не в состоянии объяснить что-либо, не исполь- зуя еще более сложных понятий и примеров, понять которые Вы уж совсем не в состоянии. Возможно, что Вам посчастливится встретиться с одним из так называ- емых "экспертов" по ОС UNIX. Слишком пожилые и утомленные, для того чтобы приносить хоть какую-нибудь пользу, они, обычно, будучи предостав- лены сами себе, немедленно начинают организовывать различные комитеты, куда выбирают друг друга, и писать умные книги. Если Вы столкнетесь с таким "экспертом", то отнеситесь к нему с почтением при случае сбегайте ему за пивом, но никогда не обращайтесь к нему за советом — это бессмыс- ленно, поскольку Вы будете с ним говорить на разных языках. 1.2. КАК ВОЙТИ В ОС UNIX Прежде чем начать работать с ОС UNIX, Вам необходимо суметь войти в нее. Для этого надо иметь: ЭВМ, которая работает под управлением ОС UNIX; терминал, подключенный к этой ЭВМ; имя, под которым Вы зарегистрированы в ОС UNIX. Имя, под которым пользователь зарегистрирован в ОС UNIX, иногда называют входным именем пользователя. Эти имена распределяются адми- нистратором системы или его доверенными. Зарегистрировать новое имя в ОС UNIX весьма просто. Однако перед тем как Вы сумеете сделать это. Вам— вне зависимости от того, собираетесь ли Вы стать администратором системы или обычным пользователем ОС UNIX, — предстоит еще многому научиться. Предположим, что Вы каким-то образом выяснили имя, под которым Вы зарегистрированы в ОС UNIX, и знаете, где расположен терминал, под- ключенный к ЭВМ, работающей под управлением ОС UNIX. Большинство современных терминалов являются терминалами дисплейного типа. Убедив- шись, что питание терминала включено. Вы должны каким-то образом до- биться, чтобы на терминал было выведено сообщение Login: Если Вам повезет, то Вы обнаружите терминал именно в таком состоянии. Однако всегда есть шанс, что тот, кто последним пользовался этим термина- *06 одержимых программистах, или "хакерах", см. книгу Дж. Вейценбаум. Воз- можности вычислительных машин и человеческий разум. Отсуждении к вычислениям: Пер. с англ. — М.: Радио и связь, 1982. — 368 с. Глава 4 этой книги называется: Наука и одержимый программист. [Прим, ред.) 12
лом, не позаботился о том, чтобы выйти из ОС UNIX в конце сеанса работы, или просто забыл это сделать. Если Вам не удается добиться вывода на тер- минал сообщения Login, то придется поискать другой терминал или обратить- ся за помощью к кому-нибудь, кто хорошо знает ОС UNIX1. В ответ на сообщение Login следует ввести с терминала имя,под кото- рым Вы зарегистрированы в ОС UNIX, завершив его ввод нажатием клавиши return2, В нормальных условиях ОС UNIX никак не будет реагировать на то, что Вы вводите с терминала, до нажатия клавиши return, которое явля- ется для ОС UNIX сигналом того, что ввод строки завершен. Иногда новички забывают об этом и, введя с терминала строку, гордо откидываются на спинку стула в ожидании появления на терминале результата, который там никогда не появится. Они забывают нажать клавишу return. Вы ведь не забудете, не правда ли? Если имя, под которым Вы зарегистрированы в ОС UNIX, введено с тер- минала правильно, то последняя или спросит Вас о пароле, или, не задавая этого вопроса, позволит сразу же войти в нее. Если ОС UNIX потребует ввода пароля, то Вы можете оказаться в затруднительном положении, если пароль не был Вам сообщен вместе с именем, под которым Вы зарегистри- рованы в ОС UNIX. Если ОС UNIX потребовала ввода пароля и Вы знаете свой пароль, то введите его с терминала, завершив ввод нажатием клавиши return. Не удивляйтесь, что пароль в процессе его ввода не отображается на терминале. Это сделано специально для борьбы с теми, кто пытается под- смотреть Ваш пароль, заглядывая через плечо с тем, чтобы впоследствии воспользоваться им в своих (скорее всего нечестных) целях. Если при вводе с терминала имени, под которым Вы зарегистрированы в ОС UNIX, была допущена ошибка, то на терминал будет выведено сообще- ние Login incorrect. Uogin: После вывода такого сообщения Вы можете предпринять еще одну попытку войти в ОС UNIX. Если две-три повторных попытки войти в ОС UNIX не привели к успеху, значит, Вы делаете что-то не так. Здесь полезно иметь в виду, что ОС UNIX предпочитает работать со строчными, а не с прописными буквами. Это означает, что она может неправильно интерпретировать имена пользователей, введенные вместо строчных прописными буквами. Кроме того, необходимо помнить, что имена пользователей, как правило, не должны содержать пробелов, У нас в ОС UNIX пользователи регистрируются обычно 1 Вы можете также попытаться дочитать до конца эту главу, а затем на основе по- лученных знаний попробовать добиться вывода на терминал требуемого сообщения. Материала этой главы вполне достаточно, чтобы суметь сделать это, если, конечно, что- нибудь не сломалось. На отечественных терминалах это обычно клавиша ВК — возврат каретки. [Прим, ред.} 13
под именами типа 'abc123'. Некоторые пользователи думают, что 'abc123' и 'abc 123'— это одно и то же, но это далеко не так. В результате успешного входа в ОС UNIX на терминал будут выведены сообщение текущего дня1, адресованное всем входящим в ОС UNIX поль- зователям, и, быть может, пара других сообщений. Давайте рассмотрим протокол входа в ОС UNIX более подробно. Типичный протокол входа в ОС UNIX выглядит у нас следующим образом: Login: mike Password: WARNING: System closing down at 13:00 for approximately one hour whilst the disc packs are being cleaned1 2. You have mail3. $ О сообщении You have mail можно совершенно безболезненно забыть на не- которое время. Однако если сейчас около 13.00, то необходимо следить за временем, поскольку ровно в 13.00 Ваш терминал будет отключен от ОС UNIX. Еще раз обращаем Ваше внимание на то, что вводимый пароль не отображается на терминале. После всех сообщений на терминал будет выве- ден промптер (prompter)4, который является признаком того, что ОС UNIX готова воспринимать вводимые Вами команды5. У нас промптером являет- ся символ $, однако у Вас может использоваться другой промптер. Если окажется, что в качестве промптера у Вас используется что-нибудь вроде ОК или Ready, то не расстраивайтесь. Скоро Вы научитесь изменять промптер по своему усмотрению. В любом случае независимо от того, что использует- ся в качестве промптера, вывод на терминал последнего означает, что ОС UNIX готова воспринимать вводимые Вами команды. Иначе говоря, пром- птер служит приглашением к вводу команды. Одной из самых простых ко- манд является команда echo. 1.3. ПРОСТАЯ КОМАНДА Команда echo выводит на терминал все, что Вы ввели с терминала. Попро- буйте применить эту команду, не забыв при этом нажать клавишу return: echo hello mike 1 Сообщение текущего дня (message of the day) хранится s файле etc/motd. (Прим. редЛ ВНИМАНИЕ: в 13.00 система будет выключена примерно на 1 ч для очистки дис- ковых пакетов. (Прим, пер} 3 Для Вас имеется почта. [Прим, пер} 4 Prompter - подсказчик. {Прим, пер) Ввиду отсутствия в отечественной литературе устоявшегося термина для обозна- чения этого понятия мы решили воспользоваться уже привычным для советских про- граммистов методом словообразования. Примером применения этого метода могут служить такие слова, как "препроцессор", "спулпинг", "дамп", "пул" и т. д. (Прим, рад.} 14
В ответ на терминал будет выведено helio mike Приятно, не правда ли? Команда, которую Вы только что употребили, типична для ОС UNIX, Она была введена с терминала в виде одной строки и состояла из имена команды echo, за которым следуют аргументы hello и mike. Аргументы отделены от имени команды и друг от друга пробелами, что также типично для ОС UNIX1. В дальнейшем мы будем часто использовать термины "имя команды" и "ар- гумент", поэтому желательно, чтобы Вы как следует усвоили их значения. За редким исключением, команды в ОС UNIX представляют собой одну строку, содержащую имя команды, за которым следует список ее аргумен- тов. Этот список иногда может быть пустым (например, при отсутствии ар- гументов) . Разделителем, как уже говорилось выше, служит пробел. Этим ОС UNIX существенно отличается от большинства существующих операцион- ных систем, в которых в качестве разделителя используется обычно запятая или, что еще хуже, комбинация запятой и пробела. Перечитайте этот параграф и убедитесь, что Вам все понятно. Если Вам все понятно, то давайте рас- смотрим три примера использования команды echo, приводимые ниже. В первом примере аргументы отсутствуют, во втором имеется один аргумент, а в третьем два: echo echo 1 argument echo 2 arguments Следствием того, что аргументы отделяются друг от друга пробелами, является то, что последние не могут встречаться внутри аргументов, а это не слишком удобное ограничение. На самом деле такого ограничения нет, вернее, существуют способы обойти ограничения такого типа. Создатели ОС UNIX старались на любой вопрос иметь возможность ответить "конечно можно", а не "только через мой труп". Вы хотите научиться,включать про- белы внутрь аргументов? Пожалуйста, это не сложно, но мы расскажем об этом не сейчас, а несколько позднее, так что Вам придется немного подо- ждать. Говоря об аргументах, необходимо предупредить, что порядок их сле- дования является важным. Не все символы, входящие в аргументы, пере- даются непосредственно команде. Поэтому лучше всего, если на первое время для формирования аргументов Вы ограничитесь только строчными и прописными буквами латинского алфавита и цифрами, не пытаясь исполь- зовать для этой цели какие-либо другие символы. Употребление определен- ных символов для формирования аргументов может привести к неожидан- ным для Вас результатам. 1 В качестве разделителя может использоваться также символ tab. (Прим.ред.) 15
1.4. ИНТЕРПРЕТАТОР КОМАНД shell Теперь мы должны пояснить то, чем Вы только что занимались. Ваши действия никоим образом не являлись обращением собственно к ОС UNIX. Попытку обратиться собственно к ОС UNIX можно сравнить с попыткой обратиться с чем-либо к облакам, причем последнее сделать, может быть, даже проще, поскольку облака Вы, по крайней мере, можете увидеть. По- пробуйте, однако, открыть центральный процессор и разглядеть внутри него ОС UNIX. В основе того, что происходило на самом деле, лежит хотя и достаточно простой, но тем не менее один из основополагающих принципов, на которых базируется ОС UNIX. Суть этого принципа состоит в том, что все, что про- исходит в ОС UNIX, реализуется обычными программами, вызываемыми точно так же, как и программы пользователей. Некоторые из этих программ являются привилегированными, как, например, та, что обеспечила Ваш bxoabOCUNIX (она называется login), и имеют специальные полномочия, однако это скорее исключение, чем правило; большинство из них — это вполне обычные программы. Когда Вы входите в ОС UNIX, то программа login проверяет, правильно ли введены имя, под которым Вы зарегистриро- ваны в ОС UNIX, и Ваш пароль. Если все в порядке, то после пары трюков программа login запускает для Вас интерпретатор команд shell, который вы- водит на терминал промптер и осуществляет интерпретацию введенной Вами с терминала строки. Получив имя команды и аргументы, интерпретатор команд shell обычно вызывает эту команду на выполнение и передает ей эти аргументы. Как он это делает, будет объяснено несколько позднее, а сейчас нас интересует, что он делает. Именно интерпретатор команд shell, а не ОС UNIX интерпретирует вводимые Вами команды, инициирует выполнение этих команд и обеспечивает передачу им введенных Вами аргументов1. Сами команды реализованы в виде отдельных программ, которые выполняются под управлением ОС UNIX. После завершения выполнения такой програм- мы интерпретатор команд shell снова выдает на терминал промптер, пока- зывающий, что он готов к приему следующей команды. Команда echo ре- ализована в виде простой программы, написанной на языке программирова- ния Си, которая выводит на терминал переданные ей аргументы. Такой подход к созданию интерпретатора команд и собственно команд можно встретить крайне редко, хотя он и обеспечивает очень высокую степень гибкости. Операционная система UNIX является одной из немно- гих операционных систем, позволяющих пользователям строить свои соб- ственные интерпретаторы команд с наиболее удобной для пользователей формой командного языка. Простота создания новых интерпретаторов 1 Авторы имеют в виду не саму ОС UNIX, а ядро ОС UNIX, однако подход "снача- ла практика, а затем теория" не позволяет им ввести здесь понятие ядра операционной системы. {Прим, ред.] 16
команд обеспечивается тем, что с точки зрения ОС UNIX последние являют- ся обычными пользовательскими программами. Поэтому вполне возмож- но, что в рамках используемой Вами версии ОС UNIX имеется несколько интерпретаторов команд, ориентированных на различные категории поль- зователей и обеспечивающих доступ к различным наборам команд1. 1.5. РУКОВОДСТВО ПРОГРАММИСТА ПО ИСПОЛЬЗОВАНИЮ ОС UNIX В распоряжении пользователя, вошедшего в ОС UNIX, имеется множе- ство команд. Большинство этих команд вместе с краткими пояснениями их назначения и использования перечислено в разд, 1 первого тома Ру- ководства программиста по использованию ОС UNIX1 2, в дальнейшем на- зываемого для краткости просто Руководством. Новички, как правило, считают стиль изложения материала в Руководстве слишком сложным и непонятным, хотя опытные пользователи находят его очень удобным. Если у Вас есть экземпляр Руководства, то посмотрите, что написано в разд. 1 по поводу команды echo. Если же у Вас нет экземпляра Руковод- ства, то, может быть. Вам удастся получить доступ к нему непосредственно с Вашего терминала. Для этого используется команда man3 * s. Попробуйте ввести с терминала man 1 echo В ответ на терминал должна быть выведена страница Руководства, соот- ветствующая команде echo. Если окажется, что Вы имеете доступ к Руко- водству непосредственно с Вашего терминала, то Вам, скорее всего, захо- чется получить более подробную информацию о самой команде .man. Для этого введите с терминала man man Чтение отдельных страниц Руководства не представляет особой сложнос- ти, если Вы знакомы с используемой в Руководстве нотацией. Каждая стра- 1 Даже в рамках стандартной версии 7 ОС UNIX, или подобной ей операционной си- стемы может оказаться более одного интерпретатора команд, например стандартный интерпретатор команд shell (называемый иногда shell Боурна — по фамилии его ав- тора (S. R. Bourne из фирмы Bell Laboratories} и интерпретатор команд cshell, разра- ботанный а Калифорнийском университете (г. Беркли}. (Прим, ред.) г Авторы имеют в виду UNIX Programmer's Manual, называемый часто для кратко- сти просто UPM. Последнее (на момент перевода книги) издание этого документа, от- носящееся к версии 7 ОС UNIX разработанной фирмой Bell Laboratories, опубликова- но в США а 1983 г. издательством Holt, Reinhart & Winston. (Прим, ped.) s Команда для доступа к Руководству непосредственно с терминапа может оказать- ся недоступной а Вашей версии ОС UNIX. В некоторых случаях имя этой команды может быть не man, a help. 17
ница Руководства имеет заголовок в виде имени команды, описываемой на этой странице. Так, страница Руководства, на которой приведено описа- ние команды echo, имеет следующий заголовок: ЕСНО(1) Единица в круглых скобках обозначает,, что данная страница относится к разд. 1 Руководства. За заголовком следуют имя команды и краткое описание того, что эта команда делает. Наиболее важным на странице Ру- ководства является раздел под названием SYNOPSIS1, который содер- жит описание использования данной команды. Для команды echo этот раздел страницы Руководства выглядит следующим образом: echo Г — nJ [arg] ... Из него следует, что для того, чтобы воспользоваться командой echo, Вы должны ввести с терминала имя команды, за которым может следо- вать необязательный аргумент -п и другие необязательные аргументы. Квадратные скобки обозначают необязательность аргумента, заключенного в них, а многоточие — произвольное число аргументов, таких же, как аргумент, стоящий слева от этого многоточия. В данном случае много- точие обозначает произвольное число необязательных аргументов. Если у Вас есть экземпляр Руководства, то попробуйте разобраться в описании команды echo, используя только что полученную информацию. Приме- ните команду echoc необязательным аргументом — пи без него,и посмот- рите, что при этом произойдет. Кроме этого, страница Руководства содержит информацию о назначении описываемой команды, сведения о том, как и для чего ее применять, информацию обо всех файлах, используемых и обслуживаемых данной командой, сообщения об ошибках, выдаваемые данной командой, и, на- конец, все известные ошибки, допущенные при ее реализации. Везде далее сравнивайте примеры использования различных команд с их описанием в Руководстве. Это поможет Вам научиться читать Руководство. Иногда мы будем ссылаться на различные команды следующим образом: echo (1). Это означает, что команда, именуемая echo, описана в разд. 1 Руководства. 1.6. ТЕРМИНАЛ Терминал является единственным средством, обеспечивающим взаимо- действие между Вами и OCUNIX. Вы используете терминал для ввода команд и вывода результатов их работы. Что можно сказать о работе за клавиатурой? Быть может. Вы один из тех счастливцев, которые никогда не делают ошибок, из тех, кто днями на- 1 SYNOPSIS — краткое описание, конспект. (Прим, пер.} 18
пролет сидит за терминалом, чьи пальцы порхают над клавишами и никогда не касаются не тех клавиш, что нужно? Скорее всего — нет. Наверное, Вы нормальный человек, один из тех, кто считает, что вырубить текст на камне намного проще, чем ввести этот же самый текст с терминала. Не отчаивайтесь, создатели ОС UNIX рассчитывали и на таких пользователей, как Вы. К сожалению, различные версии ОС UNIX и подобных ей операционных систем больше всего отличаются как раз в части их работы с терминалами. Например, для стандартной версии 7 OCUNIX терминалы дисплейного и печатающего типов не отличаются друг от друга — все они трактуются как терминалы печатающего типа, или телетайпы. При работе с терминала- ми дисплейного типа больше всего раздражает то, что, когда число строк вывода превышает число строк на экране, выводимая информация проно- сится по экрану с такой бешеной скоростью, что Вы не успеваете ничего раз- глядеть. При работе с терминалами печатающего типа этого не происходит, потому что вся выводимая информация остается на бумаге и, кроме того, быстродействие терминалов печатающего типа намного ниже. Еще одной мелочью является отработка нажатия клавиши rubout1. При использовании терминалов дисплейного типа отработать исчезновение неправильно введен- ного символа не представляет никаких трудностей — необходимо лишь осуществить возврат на один символ назад, вывести на место неправильно введенного символа символ >_j, а затем еще раз осуществить возврат на один символ назад, чтобы установить курсор на место удаленного символа. Однако в стандартной версии 7 ОС UNIX символ rubout трактуется как обычный символ, без отработки стирания на терминале. Это приводит к тому, что информация, вводимая в ЭВМ, отличается от информации, отображенной на Вашем терминале. В большинстве случаев в организациях, которые приобрели OCUNIX или подобную ей операционную систему вместе с исходными текстами са- мой операционной системы, осуществлены изменения той части операцион- ной системы, которая обеспечивает работу с терминалами. Значительная часть поставщиков ОС UNIX и подобных ей операционных систем в настоя- щее время придерживаются стандарта, принятого в версии 7 OCUNIX, но неизвестно, насколько долго сохранится такое положение дел2. Это приво- дит к парадоксальным результатам: одна и та же клавиша имеет различное функциональное назначение в различных версиях той же самой операцион- ной системы. В частности, это приводит к тому, что нажатие одной и той же клавиши обеспечивает в одной версии OCUNIX стирание последнего 1 На отечественных терминалах этой клавише, как правило, соответствует кла- виша ЗБ — забой. {Прим, ред.} 1 В 1983 г. четыре ведущие американские фирмы-производители полупроводни- ковых изделий (Intel, Zilog, Motorola, National Semiconductor) приняли в качестве стан- дартной операционной системы для своих изделий System V. (Прим, рвд.} 19
введенного символа, в другой версии ОС UNIX — прерывание выполняемой программы, и наоборот. К счастью, большинство пользователей очень быстро адаптируется к местной ситуации, так что число коллизий невелико. Как пользователю ОС UNIX Вам необходимо познакомиться с назва- ниями и функциями четырех специальных символов, приводимыми ниже: ERASE — стереть последний введенный символ; CANCEL — стереть всю введенную строку; INTERRUPT — значение этого специального символа будет объяснено позднее; EOT — эмулировать ситуацию "конец файла" (end-of-fНе) на терминале (используется для выхода из ОС UNIX). В Руководстве специальный символ CANCEL называется KILL. Мы реши- лись на переименование, поскольку в ОС UNIX имеется команда kill и их легко спутать. Надеемся, что это переименование уменьшит, а не уве- личит число коллизий. В стандартной версии 7 ОС UNIX специальным сим- волам соответствуют следующие клавиши на терминале: ERASE - # CANCEL INTERRUPT — rubout или delete EOT - CNTRL/D1. Соответствие клавиш # и (Э специальным символам ERASE и CANCEL является весьма необычным. Это результат решения, принятого очень дав- но — в доисторический для ОС UNIX период, т. е. "до рождества ОС UNIX", и почему-то оставшегося в силе по сей день. Некоторым пользователям этот выбор даже нравится. Мы считаем необходимым отметить, что нам самим этот выбор не слишком нравится. Правда, причиной этого, быть может, является то, что в той версии ОС UNIX, с которой мы работаем, для обозначения этих специальных символов используются другие клави- ши, а мы, наверное, просто привыкли к тем обозначениям, с которыми постоянно сталкиваемся. Для того чтобы успешно эксплуатировать ОС UNIX, Вам необходимо выяснить соответствие специальных символов клавишам терминала в ис- пользуемой Вами версии этой операционной системы. Попытайтесь выяс- нить это с помощью имеющейся у Вас документации, а если не удастся выяснить это самостоятельно, то обратитесь за помощью к более опытному коллеге. Скорее всего, информация по этому вопросу имеется в tty (4), т. е. в разд. 4 Руководства на странице с заголовком tty. ' CNTRL/ D обозначает нажатие клавиши D при нажатой клавише CNTRL (CONT- ROL). На отечественных терминалах клавиша CONTROL, как правило, обозначается как УС управляющий символ. [Прим, ред,} 20
Команда echo очень удобна для демонстрации использования специаль- ного символа ERASE. В меньшей степени она пригодна для демонстрации использования специального символа CANCEL. Попытайтесь ввести с тер- минала строку echo hello mike<ERXERXERXER>andy Здесь <ER> обозначает нажатие клавиши, соответствующей специальному символу ERASE. Теперь нажмите клавишу return. На терминал будет вы- ведено hello andy Вам понятно почему? Попробуйте самостоятельно воспользоваться клави- шей, соответствующей специальному символу CANCEL. Специальный символ INTERRUPT используется для реализации одной из наиболее распространенных функций в операционных системах, ориенти- рованных на работу в интерактивном режиме, хотя способ реализации этой функции является весьма необычным. Сейчас Вам достаточно лишь понять, что при нажатии клавиши, соответствующей специальному символу INTERRUPT, выполнение исполняющейся в данный момент команды прекращается, а на терминал выводится промптер интерпретатора команд shell. Предположим, что Вы запустили на трансляцию очень большую про- грамму. Размер этой программы настолько велик, что время трансляции составляет, скажем, полчаса. Через несколько минут после запуска этой программы на трансляцию Вы поняли, что в программе имеется ошибка, так что продолжение трансляции бессмысленно. В этом случае для прекра- щения трансляции Вам достаточно нажать клавишу, соответствующую специальному символу INTERRUPT. Последний из четырех специальных символов — EOT — используется для того, чтобы сообщить какой-либо программе об окончании ввода для нее. Нажатие клавиши, соответствующей специальному символу EOT, в ответ на выведенный на терминал промптер интерпретатора команд shell приведет к завершению работы последнего и выводу на терминал сообщения login. В действительности ввод специального символа EOT приводит к эмуляции ситуации "конец файла" на Вашем терминале, кото- рая воспринимается и определенным образом интерпретируется интерпре- татором команд shell. Опознав эту ситуацию, интерпретатор команд shell завершит свою работу. Нажатие клавиши, соответствующей специальному символу EOT, является хорошим способом добиться вывода на терминал сообщения login, если Вам не удалось добиться этого иным способом. Последовательное нажатие клавиш, соответствующих специальным симво- лам INTERRUPT и EOT, как правило, приводит к какому-нибудь результа- ту. Правда, вполне возможно, что результатом будет обрушившийся на Вас безудержный гнев того, кто работал на этом терминале непосредствен- но перед Вами, так как Вы уничтожили результаты двухчасовой работы 21
программы имитационного моделирования. Не расстраивайтесь слишком сильно из-за этого. Вы до конца сейчас еще не понимаете смысл ситуации "конец файла". Позднее мы расскажем об этом более подробно. А теперь подведем некоторые итоги. Итак, теперь Вы можете: войти в ОС UNIX; ввести простую команду; исправить ошибки, допущенные при вводе команды; прервать выполнение программы; выйти из ОС UNIX. Если Вы полностью удовлетворены, т. е. считаете, что действительно научились всем этим премудростям, то можете переходить к чтению сле- дующего параграфа. Мы, однако, рекомендуем Вам немного попракти- коваться. Особое внимание при этом Вам необходимо уделить входу в ОС UNIX и выходу из нее. Проделайте каждую из этих операций несколько раз. Для того чтобы обнаружить, что при смене пароля совершена ошибка. Вам потребуются более подробные сведения по этому вопросу. 1.7. ВАШ ПАРОЛЬ С точки зрения паролей и имен, под которым пользователи зарегистри- рованы в OCUNIX, эта операционная система похожа на любую другую многопользовательскую операционную систему. Имена, под которыми пользователи зарегистрированы в OCUNIX, применяются для того, чтобы последняя могла распознать каждого индивидуального пользователя. Наличие этих имен тесно связано с вопросами защиты информации. Как Вы скоро узнаете, команды OCUNIX хранятся в файлах (понятие файла вводится в гл. 2), поэтому в рамках OCUNIX все, что связано с доступом к информации, базируется на особенностях файловой системы. Исчерпы- вающие объяснения по этим вопросам будут приведены несколько позд- нее. Очевидно, что нет смысла обеспечивать защиту информации одних пользователей от других в том случае, если для осуществления несанкцио- нированного доступа к скрываемой информации необходимо знать лишь имя пользователя-владельца информации. Поэтому каждый пользователь кроме того имени, под которым он зарегистрирован в OCUNIX, имеет также только ему одному известный пароль, выступающий в роли "удосто- верения личности" при входе в OCUNIX (OCUNIX позволяет пользовате- лям самим изменять свои пароли). Наиболее популярными паролями являются имена возлюбленных, телефонные номера, номера комнат и тому подобные. К несчастью, пароли такого рода очень легко угадать, и тот, кто захочет выяснить Ваш пароль, начнет свои попытки угадать именно с них. В конечном счете Вы сами выбираете себе пароль в зависимости от конкретных обстоятельств. Если Вы являетесь единственным пользо- вателем OCUNIX, то, скорее всего. Вы сможете обойтись вообще без па- роля. Члены группы, работающие над одним проектом и доверяющие 22
друг другу, могут пользоваться одним паролем. У нас в университете часто применяются крайне замысловатые пароли, что является следствием частых попыток осуществления несанкционированного доступа со стороны любознательных студентов. Если Вам необходимо заботиться о защите.Вашей информации, то: выбирайте необычный пароль; включайте в пароль цифры и управляющие символы; никогда и никому не позволяйте наблюдать за тем, как Вы входите в ОС UNIX; регулярно меняйте пароль; следите за попытками нарушения защиты Вашей информации. Почему мы рекомендуем Вам использовать эти правила? Ответить на этот вопрос очень просто. Необычный пароль трудно угадать при случайных и непреднамеренных попытках нарушить защиту Вашей информации. Использование цифр и управляющих символов повышает степень необыч- ности пароля. Кроме того, это затрудняет выяснение пароля теми, кто подсматривает за тем, как Вы входите в ОС UNIX. Позволив кому-либо наблюдать за тем, как Вы входите в ОС UNIX, Вы тем самым предоставляе- те ему хорошую возможность узнать несколько первых символов Вашего пароля или даже весь пароль целиком и оказываете этим большую помощь тому, кто попытается разгадать Ваш пароль, используя метод 'Трубой силы", который заключается в последовательном переборе всех возмож- ных паролей. Даже если кто-то узнает Ваш пароль, затратив, возможно, на это уйму времени и сип, то весь этот труд окажется напрасным после смены Вами пароля. Достоинством частой смены пароля является, во- первых, то, что, обнаружив частую смену Вами пароля, тот, кто пытается нарушить защиту Вашей информации, быть может, оставит Вас в покое, а во-вторых, то, что каждый раз после смены пароля тот, кто нарушает защиту Вашей информации, знает только старое значение пароля, которое теперь бесполезно. Как же обнаружить, что кто-то узнал Ваш пароль? Обычно в ОС UNIX ведется протокол всех вхождений в нее. Этот протокол, как правило, содержит имя, под которым пользователь зарегистрирован в OCUNIX, дату и время входа в OCUNIX и имя использованного терминала. Если Вы, периодически просматривая этот протокол, вдруг обнаружите, что в нем зарегистрирован вход в ОС UNIX под Вашим именем, в то время когда Вы сами не работали, или, что этот вход в OCUNIX был осуществлен не с того терминала, который используете Вы сами, то это будет означать, что Ваш пароль стал кому-то известен1. Просматривая даты последнего доступа и последней модификации Ваших файлов, Вы, быть может, также В этом случае Вам, может быть, даже удастся обнаружить, что тот, кто узнал Ваш пароль, работал с другого терминала одновременно с Вами. (Прим, ред.] 23
сумеете обнаружить, что кто-то уже знает Ваш пароль, правда, в этом слу- чае, по-видимому, такая информация будет для Вас несколько запоздавшей. На самом деле имеется ряд высокоэффективных методов для "взламы- вания" паролей, применяемых ассами этого дела, расценивающими свою деятельность как своего рода интеллектуальную забаву. С их точки зрения система защиты информации существует специально для того, чтобы ее нарушать. С некоторыми из этих методов настолько трудно бороться, что о них упоминают только на ночных совещаниях, проводимых при зак- рытых дверях, так что распространение этих секретов среди участников такого совещания чем-то напоминает языческий ритуал. У нас нет намере- ний давать какие-либо указания на эти ужасные методы, которые, попав в руки случайных людей, могут быть использованы с дурными намерения- ми. Мы поступаем в соответствии со старым армейским принципом без- опасности: чем больше времени неприятель будет думать о том, как ему атаковать нас, тем меньше времени у него останется на проведение самой атаки. Необходимо отметить, что ОС UNIX не является единственной опера- ционной системой, уязвимой по отношению к атакам такого рода. Все без исключения операционные системы, использующие для идентификации пользователя комбинацию имени и пароля, находятся с этой точки зрения в одинаково уязвимом положении. На эту тему имеется одна очень инте- ресная статья, которую Вы, безусловно, сумеете найти, если только как следует поищите. Теперь поговорим .о смене пароля. Может быть, предыдущие абзацы Вас сильно напугали? Если это так, то в данный момент это даже кстати. Выберите пароль, убедившись, что Вы в состоянии запомнить его. Если Вы забудете его, то будете подвергнуты презрению и осмеянию. Растяпы, ко- торые ухитряются забыть свой пароль, постоянно служат источником развлечения для нас — тех, кто должен помочь им выйти из этой ситуации. Команда, с помощью которой Вы можете изменить свой пароль, назы- вается passwd. Это необычная команда; необычность ее заключается в том, что она проводит диалог с пользователем. Большинство команд в ОС UNIX предпочитает не задавать пользователю никаких вопросов, поскольку это затруднило бы реализацию команд в виде командных фай- лов. Причины этого станут понятны, когда Вы поближе познакомитесь с командными файлами. Обычно параметры командам передаются через их аргументы. Случаи диалога с пользователем крайне редки. Естественно, что имеется ряд программ, принципиально работающих в интерактивном режиме. Примером такой программы является редактор текстов. Для того, чтобы выполнить команду passwd, необходимо предваритель- но войти в ОС UNIX, а затем ввести с терминала passwd Это приведет к тому, что команда passwd запросит старый пароль для того, чтобы удостовериться в том, что именно Вы сами хотите сменить свой па- роль, поскольку встречаются шутники, которые пытаются сменить Ваш па- 24
роль (без Вашего ввдома, естественно) в то время, когда Вы отошли от терминала, например, чтобы покурить, не выйдя предварительно из OCUNIX. Если Вы правильно введете с терминала свой старый пароль, то команда passwd запросит новый пароль. При вводе с терминала нового пароля он не будет отображаться на терминале. Поскольку введенный Вами новый пароль не был отображен на терминале, то Вы не можете быть уве- рены в том, что не сделали ошибок при его вводе. Поэтому команда passwd еще раз запрашивает новый пароль (опять при вводе с терминала нового пароля он не будет отображаться на терминале). Если Вам удалось оба раза ввести с терминала один и тот же новый пароль, то команда passwd произведет изменение Вашего пароля со старого на новый. Команда passwd "подталкивает" Вас к использованию паролей, которые подчиняются элементарным правилам безопасности (с точки зрения крип- тографии) , требуя, чтобы пароль состоял не менее чем из четырех символов при использовании двух регистров (строчных и прописных букв) или не менее чем из шести символов при использовании только одного регистра. Однако если Вы будете достаточно настойчивы, то Вам удастся ввести более короткий пароль. Как это сделать, объяснено в разд. 1 Руководства1. Ниже приводится пример использования команды passwd: $ passwd mike Changing password for mike Old password: New password: Retype new password: $ Из приведенного выше примера видно, что ни один из вводимых Вами паро- лей не отображается на терминале. 1.8. УПРАЖНЕНИЯ 1. Какие клавиши соответствуют специальным символам ERASE, CANCEL, INTER- RUPT и EOT в Вашей версии ОС UNIX? 2. В чем заключается разнице между командами и аргументами? 3. Каким образом аргументы отделяются друг от друга и от имени команды? 4. Предположим, что Ваша программа зациклилась и выводит на печать разнообраз- ную, но совершенно бесполезную информацию. Как завершить ее выполнение? 5. Что такое интерпретатор команд shell? Чем интерпретатор команд shell отличается- от тех программ, которые лишите Вы сами? 6. Как можно выйти из ОС UNIX? Зачем необходимо выводить из ОС UNIX (после завершении сеанса работы) ? 1 Раздел 1 Руководства не содержит более подробной информации, чем имеющаяся здесь, о том, как можно добиться от команды passwd, чтобы она приняла от Вас более короткий пароль [Прим. ред.) 25
ГЛАВА 2. ФАЙЛЫ И ПРОСТЫЕ КОМАНДЫ 2.1. ФАЙЛЫ Большинство команд в ОС UNIX предназначено для работы с файлами. Информацией, хранящейся в файле, может быть, например, исходный текст программы, написанной на некотором языке программирования, или вы- полняемая программа, текст документа или учетная информация. Очевид- но, что если транслятор с какого-нибудь языка программирования попы- тается вводить информацию из файла, содержащего выполняемую програм- му, то, скорее всего, эта информация будет воспринята им как какая-то тарабарщина. Очевидно также, что из попытки вызвать на выполнение файл, содержащий учетную информацию, скорее всего, ничего хорошего не получится. В большинстве операционных систем файлы различаются в зависимости от содержащейся в них информации. В некоторых опера- ционных системах различаются даже файлы, содержащие исходные тексты программ, написанных на Фортране, и файлы, содержащие исходные тексты программ, написанных на Паскале. В ОС UNIX эти вопросы решены шире: любой файл независимо от того, какую информацию он содержит, является с точки зрения ОС UNIX последовательностью байт, к которой обеспечи- вается произвольный доступ. В ОС UNIX не налагается никаких ограничений ни на методы доступа, ни на хранимую в файлах информацию, ни на вид и структуру этой инфор- мации. Определенный смысл и структуру хранимой в файле информации придает та программа, которая осуществляет чтение или запись этой инфор- мации. В ОС UNIX не налагается никаких ограничений на имена файлов, если не считать того, что большинство трансляторов по умолчанию исполь- зует фиксированные окончания имен файлов, в которые помещаются результаты их работы1. 2.1.1. ДОПУСТИМЫЕ ИМЕНА ФАЙЛОВ Имя файла может состоять не более чем из 14 символов. В именах фай- лов разрешается использовать любые символы — строчные и прописные буквы латинского алфавита, цифры и даже управляющие символы. Здра- вый смысл подсказывает ограничиться для составления имен файлов только печатными символами. Однако поскольку некоторые из печатных символов, отличные от строчных и прописных букв латинского алфавита 1 На самом деле большинство трансляторов также требует размещения исходных текстов программ в файлах с фиксированными, в зависимости от транслятора, окон- чаниями имен. Кроме того, как это будет видно из следующего параграфа, имеется ограничение на длину имен файлов. (Прим, ред.) 26
и цифр, имеют в ОС UNIX специальный смысл, то мы предлагаем Вам пер- вое время использовать для составления имен файлов только строчные и прописные буквы латинского алфавита и цифры. 2.2. ФАЙЛОВАЯ СИСТЕМА И КАТАЛОГИ Файловая система ОС UNIX имеет иерархическую древовидную струи туру1, каждый узел которой является файлом. Структура файловой систе- мы ОС UNIX изображена на рис. 1. Как видно из рисунка, некоторые узлы не являются концевыми, а имеют ветви, ведущие к другим узлам. Подоб- ным промежуточным узлам соответствуют файлы особого типа, называе- мые каталогами. Обычные файлы могут быть лишь концевыми узлами. Концевой узел может представлять собой как обычный файл, так и каталог (в этом случае он будет пуст)г. Будучи файлом особого типа, каталог в то же время является самым обычным файлом, информация из которого может быть введена Вами точно таким же способом, как из обычного создан- ного Вами самим файла. Как пользователь ОС UNIX Вы будете иметь свой собственный основной каталог пользователя. Основной каталог пользователя — это тот каталог, который становится для пользователя текущим сразу же после входа в ОС UNIX. Что такое текущий каталог, будет объяснено немного позднее. Как правило, каждый пользователь ОС UNIX имеет свой собственный основной каталог пользователя. Однако бывают случаи, когда несколько пользователей ОС UNIX имеют один и тот же каталог в качестве своего основного каталога пользователя. Когда Вы создаете новый файл, а Вы скоро научитесь это делать, то информация об этом файле заносится в неко- торый каталог. В этом случае обычно для простоты говорят, что файл создан в таком-то каталоге. Единственной связью между файлом, создан- ным Вами в некотором каталоге, и файлом с таким же именем в каком- либо другом каталоге является то, что оба эти файла являются различными узлами одной и той же иерархической древовидной структуры. Вы можете сами создавать новые каталоги в своем основном каталоге пользователя. Эти каталоги, в свою очередь, могут содержать как обычные файлы, так и каталоги, что обеспечивается иерархической древовидной структурой файловой системы ОС UNIX. Такая возможность позволяет группировать файлы в зависимости от содержащейся в них информации и размещать различные группы файлов в различных каталогах. Основные каталоги пользователей содержатся, как правило, в каталоге /usr, а тот. 1 Строго говоря, структура файловой системы ОС UNIX не является деревом в чистом виде. (Прим. ред.) 1 Концевые узлы могут представлять собой и специальные файлы, о которых идет речь в гл. 5. (Прим, ред.) 27
(rout) bln etc usr t/np dev Рис. 1. Иерархическая древовидная структу- ра файловой системы echo sb Jane Andy Mike Ip ttyO unixbeok srs bin chap 1 shapZ в свою очередь, — в корневом каталоге (корне иерархической древовидной структуры), имя которого обознача- ется символом Z1. Для того, чтобы найти в файловой системе ОС UNIX какой-либо файл, необходимо указать путь по дереву до этого файла. Этот путь называется полным именем файла, в отличие от собственно имени файла (того имени, под которым файл занесен в каталог). Полные имена файлов в ОС UNIX состоят из некоторого числа слогов, отделяемых друг от друга с помощью символа /. При чтении полного имени файла слева направо каждый его слог, кроме последнего, т. е. самого правого, является именем каталога, в который занесен следующий за этим слогом слог. Последний слог в полном имени файла представляет собой собственно имя файла в каталоге, имя которого является предпоследним слогом полного имени файла. Файл, имя которого — последний слог полного имени.файла, может быть как обычным файлом, так и каталогом2. В качестве примера рассмот- рим полное имя файла /bin/echo. С помощью этого полного имени файла можно получить доступ к обычному файлу, занесенному под именем echo в каталог bin, содержащийся, в свою очередь, в корневом каталоге /. Этот файл содержит выполняемую программу, обеспечивающую работу команды echo. Полные имена файлов могут начинаться и не с символа /, являющегося именем корневого каталога. Если полное имя файла начи- нается не с символа /, то поиск файла начинается от текущего каталога, а не от корневого каталога. 2.3. ВЫВОД СОДЕРЖИМОГО КАТАЛОГОВ НА ТЕРМИНАЛ По-видимому, одной из наиболее важных процедур, которые Вы можете проводить с каталогами, является вывод на терминал их содержимого. Сама по себе возможность хранения файлов стоит не многого при отсут- ствии способа ознакомиться с их содержимым. 1 Более подробно особенности файловой системь! и механизмы ее функциони- рования будут рассмотрены е гл. 5. 1 Файл, имя которого является последним слогом в полном имени файла, может также представлять собой специальный файл. Специальные файлы будут рассмотрены в гл. 5. [Прим. ред.). 28
Команда Is выводит на терминал содержимое каталогов, имена которых указаны в качестве ее аргументов. При отсутствии аргументов, специфи- цирующих имена каталогов, команда 1s выводит на терминал содержимое текущего каталога. С помощью команды 1s Вы можете вывести на терминал только имена файлов, а не их содержимое. Использование команды 1s без аргументов приводит к выводу на терминал в алфавитном порядке имен всех файлов, содержащихся в текущем каталоге1. В некоторых версиях ОС UNIX и подобных ей операционных систем эта команда называется dir или list. Если Вы примените команду 1s без аргументов сразу после первого входа в ОС UNIX, то Вы увидете на терминале следующее: $ Is $ Вас интересует, почему на терминал не было выведено содержимое Вашего основного каталога пользователя? Логично было бы ответить на этот воп- рос так: Ваш основной каталог пользователя пуст, поскольку Вы сами еще не создали в нем ни одного файла1 2. Однако такой ответ не совсем правилен, поскольку при создании любого каталога в него помещаются имена двух файлов, не выводимые на терминал командой 1s в обычном режиме. Именами файлов, заносимыми в любой каталог при его создании, являются . и . . . Прежде чем перейти к описанию различных режимов выполнения команды 1s, рассмотрим, что собой представляют файлы с именами . и .. . Имя файла . соответствует самому каталогу. Оно используется коман- дой 1s в том случае, когда она употребляется без аргументов, специфици- рующих имена каталогов, как, например, в приведенном выше примере. Имя файла . . соответствует каталогу, в котором содержится данный ката- лог. В Вашем основном каталоге пользователя имя файла .. соответствует каталогу /usr. Таким образом, можно говорить, что имя файла . всегда соответствует текущему каталогу, а имя файла . . — родительскому каталогу или, иначе говоря, каталогу-предку текущего каталога3. 1 Как Вы увидите немного позднее, в этом случае команда 1s выводит на терминал не все имена файлов, содержащиеся в текущем каталоге. (Прим, ред.). а Возможно, что Вы обнаружите в своем основном каталоге пользователя какие- нибудь файлы, созданные в ходе регистрации в ОС UNIX Вашего имени. В этом случае назначение этих файлов Вы сможете выяснить в имеющейся у Вас документации или, в крайнем случае, у администратора системы. 2 Особым случаем является файл с именем . . в корневом каталоге с именем /, поскольку в данном случае родительский каталог отсутствует. При этом файл с име- нем . . , как и файл с именем . , соответствует корневому каталогу / . (Прим, ред.) 29
Одной из важнейших особенностей команд в ОС UNIX является воз- можность использования в них флагов. Флаг — это аргумент команды, специфицирующий режим ее выполнения. Одновременное использование в команде нескольких флагов (конечно, не взаимоисключающих) позво- ляет выполнять команды в режиме, который представляет собой комбина- цию режимов, соответствующих каждому их этих флагов. По установлен- ному соглашению любой флаг начинается с символа — В команде 1s могут быть использованы различные флаги. Среди флагов, воспринимае- мых командой Is, имеются флаги —i и —а. Использование флага —I при- водит к выводу на терминал содержимого каталога в так называемом длинном формате, а использование флага —а — к выводу на терминал всех без исключения имен файлов, содержащихся в каталоге. Если теперь, зная смысл флагов —I и —а. Вы примените команду Is — al то на терминал будет выведено примерно следующее: drwxr-xr-x 2 andy 32 Dec 25 14:34 . drwxr-xr~x 3 bin 1186 Dec 25 14:38 .. при условии, что текущим каталогом является тот же самый каталог, что и ранее. Таким образом, каталог, который Вы ранее считали пустым, на са- мом деле, как только что обнаружилось, содержит два имени файла — . и . . . Нетрудно видеть, что на терминал выведены времена и даты послед- ней модификации каждого из этих файлов. Числа 32 и 1186 соответствуют числу байт в каждом из этих файлов, a andy и bin определяют имена вла- дельцев каждого их этих файлов, т. е. имена, под которыми владельцы этих файлов зарегистрированы в ОС UNIX. Комбинация символов rwxr-xr-x называется кодом защиты файла. Код защиты файла определяет, кто имеет доступ к данному файлу и каки- ми правами доступа при этом обладает. Более подробно о коде защиты файла будет рассказано немного позднее. Предшествующий комбинации символов rwxr—хг—х символ d свидетельствует о том, что файл является каталогом. Цифры 2 и 3, стоящие после кода защиты файла, являются значениями счетчиков числа ссылок на каждый из этих файлов. В ОС UNIX один и тот же файл может фигурировать в различных каталогах под различ- ными или одними и теми же именами. Более того, ОС UNIX позволяет одному и тому же файлу фигурировать в одном каталоге под различными 1 На самом деле в ОС UNIX существуют команды, флаги которых начинаются не с символа —. Примером такой команды может служить команда awk. {Прим, ред.) 30
именами1. В результате возникает понятие счетчика числа ссылок на файл. Сейчас не стоит ломать себе голову, пытаясь более глубоко постигнуть смысл этого1. 2.4. СОЗДАНИЕ КАТАЛОГОВ И СМЕНА ТЕКУЩЕГО КАТАЛОГА Как уже говорилось ранее, Вы можете создавать новые каталоги в своем основном каталоге пользователя, который первоначально был создан для Вас администратором системы в процессе регистрации им Вашего имени в ОС UNIX. Как же Вам создать новый каталог в своем основном каталоге пользователя? Очень часто бывает важно обеспечить размещение ряда файлов в отдель- ном каталоге. Рукопись этой книги была напечатана с использованием средств форматирования текстов, входящих в состав стандартного набора инструментальных средств ОС UNIX. Все файлы, содержащие подготовлен- ный для. форматирования текст рукописи этой книги, помещались в ката- логе unixbook. Этот каталог был создан в основном каталоге пользователя с помощью команды mkdir unixbook которая была использована непосредственно после входа в ОС UNIX. После того как каталог unixbook создан, установить его в качестве текущего каталога можно с помощью команды cd unixbook1 * 3 Установку некоторого каталога в качестве текущего называют переходом в этот каталог. Поскольку основной каталог пользователя по отношению к каталогу unixbook является родительским каталогом, т. е. имя файла.. в каталоге unixbook соответствует основному каталогу пользователя, то перейти обратно из каталога unixbook в основной каталог пользователя можно с помощью команды cd .. Команда mkdir для создания нового каталога использует системный вызов4. Вы можете создавать столько новых каталогов, сколько захотите. Однако мы рекомендуем Вам подходить к вопросу создания новых ката- логов разумно, так как обычно существует верхний предел для выделяемо- 1 При этом нарушается древовидность структуры файловой системы ОС UNIX. [Прим, ред.} 1 Более подробно этот вопрос будет рассмотрен в гл. 5. 3 В версии 6 ОС UNIX эта команде называлась chdir. [Прим. ред.) 4 Системный вызов — это способ взаимодействия программ пользователей с ОС UNIX. 31
го каждому пользователю места в файловой системе, а каталоги, так же как и обычные файлы, занимают это место. Команда cd позволяет, переходя из каталога в каталог, перемещаться по дереву файловой системы. Это одна из немногих команд, выполнение которых не приводит к выполнению какой-либо программы. Интерпрета- тор команд shell, опознав эту команду, выполняет системный вызов, обеспечивающий смену текущего каталога на каталог, полное имя которого является аргументом команды cd. Команда cd очень полезна при работе с файлами из различных каталогов, поскольку ее использование позволяет избежать указания длинных полных имен файлов, вместо которых можно употреблять короткие собственно имена файлов. Рассмотрим это на примере, для чего вернемся к рис. 1, Пусть пользова- тель, для которого основным каталогом пользователя является каталог jane, хочет получить доступ к файлу chapl, содержащемуся в каталоге unixbook, а тот, в свою очередь, — в основном каталоге пользователя andy. В этом случае он должен указать полное имя файла одним из двух способов: в виде /usr/andy/unixbook/chap1 или в виде . . /andy/unixbook/ chapl. В первом случае полное имя файла является абсолютным полным именем файла, поскольку оно указывает полный путь по дереву, т. е. путь, начинающийся в корневом каталоге и заканчивающийся собствен- но именем файла. Во втором случае полное имя файла является относитель- ным полным именем файла, поскольку оно указывает не полный путь по дереву, начинающийся в корневом каталоге, а путь по дереву от некоторого каталога, являющегося в данный момент текущим. Таким образом текущий каталог представляет собой тот каталог, начиная с кото- рого производится поиск файла в случае указания относительного пол- ного имени файла. Особенно утомительным ввод полных имен файлов становится при необходимости доступа к ряду файлов, находящихся в од- ном и том же каталоге. Альтернативой использованию длинных полных имен файлов является однократное употребление команды cd с тем, чтобы сначала перейти в требуемый каталог, например в каталог /usr/andy/unix- book, а затем уже, установив требуемый каталог в качестве текущего каталога, использовать короткие собственно имена нужных файлов: $ cd /usr/andy/unixbook $ Is -1 chapl $ cat chap2 Команда cat будет рассмотрена несколько позднее. Вы можете переходить из одного каталога в другой, если: известно полное имя (абсолютное или относительное) того каталога, в который Вы хотите перейти; права доступа, которыми Вы располагаете, таковы, что механизм защиты файлов, используемый в файловой системе ОС UNIX (описываемый в следующем параграфе), позволяет сделать это. 32
Если, перемещаясь с помощью команды cd по дереву файловой системы. Вы Забыли, какой каталог является текущим в данный момент, то не пу- гайтесь, а воспользуйтесь командой pwd для того, чтобы узнать имя текущего каталога. Команда pwd обеспечивает вывод на терминал абсолют- ного полного имени текущего каталога. Так, если в данный момент теку- щим каталогом является unixbook, то, если Вы введете с терминала коман- ду pwd, в результате ее выполнения на терминал будет выведено /us r/andy/un i xboo k 2.5. МЕХАНИЗМ ЗАЩИТЫ ФАЙЛОВ В ФАЙЛОВОЙ СИСТЕМЕ ОС UNIX Хорошая файловая система должна обеспечивать, с одной стороны, воз- можность совместного использования одних и тех же файлов различными пользователями, а с другой стороны, защиту файлов от несанкционирован- ного доступа1. Файловая система ОС UNIX различает три типа доступа к файлам: чтение из файла, запись в файл и выполнение файла. Соответственно и механизм защиты файлов в файловой системе ОС UNIX обеспечивает защиту файлов по каждому из этих типов доотупа. Важность наличия механизма защиты файлов трудно переоценить. Так, например, Вы, наверное, были бы очень недовольны, если бы какие-либо другие пользователи кроме Вас самого имели возможность удалять Ваши файлы. Кроме того, существует ряд файлов, используемых ОС UNIX, доступ к которым со стероны пользовате- лей должен быть ограничен только чтением. Примером может служить файл /etc/paswd, содержащий информацию обо всех пользователях, заре- гистрированных в ОС UNIX. По отношению к любому файлу файловая система ОС UNIX помимо трех упомянутых выше типов доступа к файлам различает три категории пользо- вателей, каждая из которых имеет свои собственные ограничения по любо- му из типов доступа к файлу. Этими категориями пользователей являются: владелец (владелец файла), члены группы (член^1 группы, к которой при- надлежит владелец файла) и прочие пользователи (все остальные пользо- ватели) . Владельцем файла, до тех пор пока принадлежность файла не будет искусственно изменена, считается пользователь, создавший этот файл. Групповое владение файлами позволяет обеспечить каждому члену группы, работающей над одним общим проектом, возможность осуще- ствлять контролируемый доступ к файлам других членов этой группы. Члены группы наряду с именами, под которыми они зарегистрированы в ОС UNIX, имеют общее групповое имя. Пользователь может состоять одновременно членом различных групп и производить смену своей "теку- щей" группы на любую другую, членом которой он состоит. Использование 1 2 1 См. [1], с. 76, 81-83. 2 ак 1165 33
в команде Is флага —g приводит к выводу на терминал группового имени вместо имени владельца, т. е. вместо имени владельца на терминал будет выведено имя группы, членом которой являлся владелец в момент созда- ния файла. Таким образом, применение команды Is —lg /usr/andy/unixbook приводит к выводу на терминал примерно следующей информации: -rw-r-------------- 1 staff 1237 Dec 20 12:04 chapter2 -rw-r-------------- 1 staff 56043 Dec 30 09:47 chapters -rw-r-------------- 1 staff 196 Jan 4 14:32 index Групповое имя staff, стоящее в поле имени владельца, свидетельствует о том, что в момент создания этих файлов владелец каждого из них являл- ся членом группы staff. Коды защиты всех этих файлов установлены таким образом, что позволяют владельцу осуществлять доступ к ним по чтению и по записи, членам группы — только по чтению, а для прочих пользователей любой доступ к этим файлам запрещен. Давайте рассмотрим код защиты файла более подробно, применив команду Is—I для вывода на терминал содержимого некоторого гипоте- тического каталога: -rwx rwx rwx 1 fred 1247 Dec 25 14:13 garbage J- разрешение на доступ к файлу для прочих пользователей разрешение на доступ к файлу для членов группы разрешение на доступ к файлу для владельца г — разрешение на доступ к файлу по чтению, если вместо символа г стоит символ —, то разрешения на доступ к файлу по чтению нет; w — разрешение на доступ к файлу по записи, е^ли^рместо символа w стоит символ —, то разрешения на доступ к файлу по чтению нет; х — разрешение на доступ к файлу по выполнению, если вместо символа х стоит символ —, то разрешения на доступ к файлу по выполнению нет. В этом каталоге содержится файл garbage, владельцем которого являет- ся пользователь, зарегистрированный в ОС UNIX под именем fred. Код защиты этого файла установлен таким образом, что для каждой из трех категорий пользователей, различаемых файловой системой ОС UNIX, раз- решены все типы доступа: по чтению, записи и выполнению. Следующий пример иллюстрирует другое значение кода защиты файла, владелец которого зарегистрирован в ОС UNIX также под именем fred: $ Is -1 moregarbage — rw —г----г---- 1 fred 684 Jan 4 11:30 moregarbage В приведенном примере код защиты файла установлен таким образом, что владельцу обеспечивается доступ к нему по чтению и доступ по записи, 34
а членам группы и прочим пользователям обеспечивается доступ только по чтению. Для файлов, являющихся каталогами, код защиты файла имеет несколь- ко иной смысл, чем для обычных файлов. Рассмотрим смысл кода защиты файла в этом случае более подробно. Для того чтобы с помощью команды 1s получить информацию собственно о каталоге, а не о содержащихся в этом каталоге файлах, необходимо использовать флаг —d: $ Is -Id unixbook drwxr-xr----- 2 andy 32 Nov 10 16:03 unixbook $ Is - Igd unixbook drwxr —xr---- 2 staff 32 Nov 10 16:03 unixbook d — файл является каталогом, если вместо символа d стоит символ —, то это обычный файл; г — разрешение осуществлять вывод на терминал содержимого каталога, если вместо символа г стоит символ —, то разрешения осуществлять вывод на терминал содержимого каталога нет; w — разрешение создавать файлы в каталоге и удалять их из него, если вместо символа w стоит символ —, то разрешения создавать файлы в ката- логе и удалять их из него нет; х — разрешение на доступ к файлам, содержащимся в каталоге, и на установку каталога в качестве текущего каталога с помощью команды cd, если вместо символа х стоит символ —, то разрешения на доступ к фай- лам, содержащимся в каталоге, и на установку каталога в качестве текуще- го каталога с помощью команды cd нет. Таким образом, из приведенного выше примера следует, что члены группы staff могут осуществлять вывод на терминал содержимого ката- лога unixbook, осуществлять доступ к файлам, содержащимся в этом каталоге, устанавливать этот каталог в качестве текущего, но не могут создавать файлы в этом каталоге и удалять их из него, а прочие пользовате- ли имеют возможность осуществлять лишь вывод на терминал содержи- мого этого каталога. Изменить код защиты файла, владельцем которого Вы являетесь, можно с помощью команды chmod: chmod mode file...... Аргумент mode задает новый код защиты файла, специфицируемого аргу- ментом file. Аргумент mode может быть задан в символическом виде или в виде восьмеричной константы. Сейчас мы рассмотрим только символи- ческое представление аргумента mode (задание его в виде восьмеричной константы будет рассмотрено позднее). В общем случае символическое представление аргумента mode имеет следующий вид: [ugoa] [ + - = ] [rwxstugo] 2 35
где [ugoa] u — владелец g — члены группы о — прочие пользователи а — все [+-=] + — добавить разрешение доступа ---удалить разрешение доступа = — заменить разрешение доступа [ rwxstugo] г — доступ по чтению w — доступ по записи Хх — доступ по выполнению s — установить идентификатор владельца или идентификатор группы (см. гл. 5) t — сохранить разделяемый сегмент чистого кода (см. гл. 5) ugo — сохранить имеющиеся значения кода защиты файла для указанной категории пользователей или их комбинации Приведенная здесь обобщенная форма символического представления аргумента mode соответствует нотации, используемой в Руководстве. Давайте применим информацию, полученную об аргументе mode на практике. Для этого воспользуемся файлом garbage. Как Вы, наверное, помните, код защиты этого файла выглядел следующим образом: - rwxrwxrwx 1 fred 1247 Dec 25 14:13 garbage Простейшим способом защитить ВаШи файлы от порчи их кем-либо являет- ся запрет для других пользователей доступа к Вашим файлам по записи. Этот способ не обеспечивает стопроцентной "защиты от дурака", но в боль- шинстве случаев предотвращает случайное уничтожение информации в фай- лах. Для того чтобы защитить файл от возможного удаления другими поль- зователями, необходимо запретить всем, кроме владельца, доступ по записи в каталог, содержащий данный файл. Если вернуться немного назад и освежить в памяти значение кода защиты файла для каталогов, то станет понятно, что возможность удаления файлов из некоторого каталога опре- деляется только тем, имеется ли доступ по записи к этому каталогу, т. е. имеется ли возможность изменять содержимое этого каталога, а отнюдь н е разрешением доступа по записи к самому файлу. Информация о файле, имеющаяся в каталоге, является единственной связью между именем файла и собственно файлом. Уничтожив информацию о файле в каталоге. Вы тем самым уничтожите и сам файл. Чтобы запретить доступ по записи к файлу garbage членам группы и про- чим пользователям, необходимо ввести следующую команду chmod go—w garbage 36
Для того, чтобы узнать результат выполнения этой команды, можно вос- пользоваться командой Is —I garbage которая приведет к выводу на терминал примерно следующего: -rwxr-xr-x 1 fred 1247 Feb 10 20:41 garbage Если Вам непонятно, почему использование в качестве аргумента mode последовательности символов go—w приводит к запрету доступа к файлу по записи для членов группы и прочих пользователей, то еще раз внима- тельно изучите обобщенную форму представления аргумента mode. Следующий пример иллюстрирует запрет какого бы то ни было доступу к файлу для всех пользователей, кроме владельца и членов его группы, поскольку в аргументе mode за знаком — ничего не следует: $ chmod о= garbage $ Is -1 garbage -rwxr-x-------- 1 fred 1247 Feb 10 20:51 garbage $ Вы должны быть очень осторожны при изменении кодов защиты файлов, являющихся каталогами, особенно при изменении кода защиты Вашего основного каталога пользователя. Не забывайте о том, что воз- можность перехода в каталог с помощью команды cd требует наличия разрешения доступа по выполнению к этому каталогу. Уберите в Вашем ос- новном каталоге пользователя разрешение доступа по выполнению для владельца — ипоивет! Вам больше не удастся войти в ОС UNIX, после того как Вы из нее выйдете. 2.6. СТАНДАРТНЫЙ ВВОД-ВЫВОД И ЕГО ПЕРЕНАЗНАЧЕНИЕ Вы уже знакомы с командой echo, которая выводит на терминал свои аргументы. В соответствии с принятой в ОС UNIX терминалогией команда echo выводит результаты своей работы на стандартный вывод. Большинство команд в ОС UNIX производят вывод результатов своей работы на стандартный вывод. Некоторые из них осуществляют ввод со стандартного ввода — клавиатуры терминала. Одной из возможностей, предоставляемых интерпретатором команд shell, является переназначение стандартного ввода-вывода. В ОС UNIX имеется возможность разместить в одной строке более одной команды (как это сделать. Вы узнаете немного позднее); поэтому мы будем называть строку, содержащую одну или более команд, команд- ной строкой. Результаты работы команды могут быть выведены в некоторый файл вместо вывода на терминал путем добавления в командную строку после команды символа >, за которым должно следовать имя этого файла. Если 37
файл с указанным именем еще не существует, то он будет создан. Попро- буйте ввести с терминала echo Mary had a little lamb > poem Что-нибудь вывелось на терминал? Мы надеемся, что-нет. Что же произош- ло? Был создан файл роет, в который было занесено 23 символа: после- довательность символов Магу had a little lamb и символ linefeed. В ОС UNIX принято обозначать конец строки с помощью символа linefeed. Если файл роет уже существовал, то его старое содержимое будет утеряно. Вы еще не знаете, как ознакомиться с содержимым этого файла, но не расстраивайтесь — скоро научитесь и этому. А сейчас попробуйте ввести с терминала echo > junk Команда echo, которую Вы только что использовали, не имела аргументов, поэтому логично, чтобы файл junk был пустым, т. е. ничего не содержал. Это легко проверить с помощью команды Is —I,которая выводит на терми- нал примерно следующее: -rw-rw-rw- 1 andy 0 Dec 29 15:26 junk -rw-rw-rw- 1 andy 23 Dec 29 15:20 poem Действительно, файл junk содержит 0 байт. Приведенный выше пример показывает, что могут существовать пустые файлы, т. е. файлы, не содер- жащие ни одного байта информации. Использование символа > для переназначения стандартного вывода обеспечивает помещение информации в файл, начиная с начала этого файла, что приводит к потере ранее содержавшейся в этом файле информации. В ОС UNIX предусмотрен способ, как избежать этого за счет помещения новой информации в конец файла, после последнего уже записанного в этот файл байта. В этом случае для переназначения стандартного вывода используются символы », за которыми должно следовать имя файла. При этом если файл с указанным именем ранее не существовал, то он будет создан. Для того чтобы добавить вторую строку в файл роет, вве- дите с терминала echo Its fleece was white as snow » poem Теперь уж Вам точно захочется посмотреть содержимое файла, с которым Вы только что работали. Для вывода содержимого файлов на терминал, как правило, используется команда cat, обеспечивающая объединение в указанной последовательности нескольких отдельных файлов в один большой файл и вывод результатов своей работы на стандартный вывод. Таким образом, для того чтобы объединить в определенной последователь- ности несколько отдельных файлов в один большой файл, достаточно использовать команду cat f1 f2 f3 f4> bigfile 38
Для того чтобы вывести содержимое файла роет на терминал, также вос- пользуемся этой командой: $ cat poem Mary had a little lamb Its fleece was white as snow $ Переназначение стандартного ввода производится точно таким же образом, как и переназначение стандартного вывода, за исключением того, что вместо символа > используется символ <, за которым точно так же должно следовать имя файла. После переназначения стандартного ввода последний производится не с клавиатуры терминала, а из файла с указанным именем. Рассмотрим переназначение стандартного ввода на примере команды mail, обеспечивающей передачу сообщений другим пользователям ОС UNIX. Команда mail используется с одним аргументом, специфицирующим получателя сообщения. После ввода с терминала командной строки mail username следует ввести с терминала собственно текст сообщения, завер- шив его ввод нажатием клавиши, соответствующей специальному символу EOT. Специфической особенностью команды mail является то, что возмож- ность изменить или исправить текст сообщения после начала его ввода отсутствует. Чтобы избежать ошибок в тексте сообщения (поскольку Вы лишены возможности их исправить), можно сначала создать файл, содержащий текст сообщения, а затем, при вводе с терминала команды mail, переназна- чить стандартный ввод команды mail на этот файл. Попробуйте передать сообщение самому себе. Для этого создайте файл, например message, в который поместите текст сообщения, например последовательность символов Hello mike. Вы уже умеете делать это с помощью команды echo, переназначая стандартный вывод. Затем введите mail Ваше—имя < message Это приведет к тому, что команда mail будет вводить текст сообщения из файла message, вместо того чтобы вводить его с клавиатуры терминала. Используя команду mail таким образом. Вы сможете проверить и откоррек- тировать текст сообщения до его передачи. Для того чтобы передать сообщение другому пользователю ОС UNIX, замените в предыдущем примере Ваше—имя на имя, под которым заре- гистрирован в ОС UNIX тот, кому Вы хотите передать сообщение. Теперь попробуйте выйти из ОС UNIX и снова войти в нее. Проделав это, Вы обнаружите на терминале строку You have mail. Для того чтобы вывести на терминал текст полученного сообщения, введите команду mail без аргументов. В ответ на терминал будет выведена примерно следующая информация:
From Ваше—имя Wed Dec 30 15:11:30 1981 Hello mike Save? Курсор экрана, или печатающая головка (или каретка), устанавливается сразу же после вопросительного знака, так как команда mail ждет от Вас ответа на заданный ею вопрос. Если Вы ответите yes, то сообщение будет сохранено в файле mbox для последующего использования. Если же Вы ответите по, то (в данном случае это будет лучшим ответом) текст сообще- ния будет утерян. Приведенный выше пример использования команды mail позволяет Вам с помощью передачи сообщений самому себе реализо- вать простой, но весьма действенный "напоминальник". 2.7. УДАЛЕНИЕ ФАЙЛОВ И КАТАЛОГОВ По-видимому, теперь Вас интересует, как удалить все те файлы, которые были созданы Вами при воспроизведении предыдущих примеров, посколь- ку они Вам больше не нужны, а только занимают место в файловой сис- теме. Для удаления файлов используется команда rm. Удалить файлы junk и message можно, введя с терминала команду rm junk message Для удаления каталогов используется команда rmdir. Удалить каталог не удастся, если он не пуст, т. е. содержит какие-либо файлы, кроме файлов с именами . и .. . Следует взять себе за правило удалять файлы и каталоги немедленно после того, как они перестают быть Вам нужны. Своевременное удаление ненужных файлов и каталогов обеспечивает минимизацию места, занижаемого Вашими файлами и каталогами в файловой системе, и позво- ляет тем самым постоянно поддерживать хорошие отношения с админи- стратором системы. 2.8. КОПИРОВАНИЕ И ПЕРЕИМЕНОВАНИЕ ФАЙЛОВ Итак, Вы научились создавать файлы, заполнять и дополнять их, исполь- зуя команды интерпретатора команд shell и предоставляемую им возмож- ность переназначения стандартного ввода-вывода. Вы также научились удалять файлы. Однако остались еще две важные операции над файлами, которые Вы еще не умеете производить: копирование и переименование файлов. Скопировать один файл в другой можно с помощью команды ср, при этом первый файл не изменяется, т. е., с одной стороны, не изменяется его содержимое, а с другой стороны, он не уничтожается. Введя с терминала команду ср poem new—роет Вы создадите копию файла роет, имеющую имя new—роет. Содержимое этих двух файлов будет абсолютно одинаковым. Осторожно: если файл new—роет уже существовал, то его старое содержимое будет утеряно. 40
Проверить идентичность содержимого двух файлов можно с помощью команды diff. Проверить совпадение размеров двух файлов можно с помощью команды 1s $ Is -I — rw — rw — rw — 1 andy 52 Dec 31 11:03 new —poem — rw — rw — rw — 1 andy 52 Dec 29 15:20 poem $ diff poem new-poem $ Как видно из приведенного выше примера, оба файла действительно одина- кового размера. Команда diff сравнивает два файла и выводит на терминал найденные различия, а в случае совпадения этих файлов — ничего не выво- дит на терминал. Использование команды diff исключает необходимость применения команды 1s для сравнения размеров двух файлов. Копирование файлов осуществляется с помощью команды ср, имеющей в общем случае следующую форму: ср fПе1 file2 Если аргумент file2 специфицирует каталог, то для помещения копии файла в этом каталоге будет создан файл с именем f Пе1. Переименование файлов производится с помощью команды mv. Эта команда имеет в общем случае следующую форму: mv filel file2 Использование этой команды приводит к тому, что имя файла, специфи- цированное аргументом f ilel, заменяется на имя, специфицированное аргу- ментом file2. При этом файл с именем, специфицированным аргументом filel, удаляется. В разд. 1 Руководства содержится подробное описание особенностей использования команды mv. 2.9. КАНАЛЫ И ФИЛЬТРЫ Использование еще одной возможности интерпретатора команд shell позволяет замкнуть стандартный вывод одной команды на стандартный ввод другой команды. В результате образуется конвейер — командная строка, в которой стандартный вывод предшествующей команды замкнут на стандартный ввод последующей команды с помощью символа I, обеспе- чивающего создание канала между командами. Каналы являются очень полезным средством ОС UNIX, которое было заимствовано из нее в другие операционные системы. Предположим, что Вы хотите вывести на терминал информацию, содер- жащуюся в файлах dat1, dat2, dat3 и dat4 так, как будто эта информация находится в одном файле. Одним из способов сделать это является объеди- нение этих файлов в один большой файл с помощью команды cat и после- дующий вывод содержимого этого большого файла на терминал с помощью команды рг: 41
cat datl dat2 dat3 dat4 > tempfile pr -h Test-Data tempfile rm tempfile Команда pr обеспечивает вывод на стандартный вывод одного или более файлов. В команде рг возможно использование ряда флагов, специфи- цирующих формат вывода. Флаг —h обеспечивает использование командой рг аргумента, следующего за этим флагом (в данном случае это после- довательность символов вида Test—Data), в качестве заголовка каждой страницы вывода. Более подробную информацию о команде рг Вы найдете в разд. 1 Руководства. Способ, использованный выше, имеет тот недостаток, что в нем, вообще говоря, производятся два лишних действия: создание и удаление файла tempfile. Этих действий можно избежать, если, используя канал, замкнуть стандартный вывод команды cat на стандартный ввод команды рг и обра- зовать конвейер cat datl dat2 dat3 dat4 I pr—h Test-Data Команда cat. вводя последовательно четыре файла, объединяет их в указан- ном порядке в один большой файл и выводит результаты своей работы на стандартный вывод. Через качал результаты работы команды cat посту- пают на стандартный ввод команды рг. Как Вы узнаете позднее, это можно сделать еще короче. Каналы используются для конструирования командных строк на основе существующих команд ОС UNIX. Например, если Вы введете с терминала командную строку Is I wc —I то на терминал будет выведено число файлов в теущем каталоге. Команда wc обычно применяется для подсчета числа строк, слов и сим- волов в одном или более файлах, чьи имена указаны в качестве аргументов этой команды, или, в случае отсутствия аргументов, — в стандартном вводе. В приведенном выше примере флаг —I сообщает команде wc, что необхо- дим подсчет только числа строк. Другими флагами команды wc являются —w и —с. Использование флагов —w и —с позволяет подсчитывать только число слов и только число символов соответственно. Естественно, что возможно использование различных комбинаций этих флагов. Например, для подсчета числа строк и числа символов в файле af ile необходимо ввести с терминала команду wc — 1с afile Для того чтобы продемонстрировать работу команды wc. применим ее к созданным нами ранее файлам роет и new—роет: 42
$ wc poem new - poem 2 11 52 poem 2 11 52 new —poem 4 22 104 total $ В первом столбце расположена информация о числе строк, во втором — о числе слов, а в третьем — о числе символов. Первые две строки содержат информацию, соответствующую специфированным в качестве аргументов команды wc файлам, а третья строка содержит суммарные значения по каждому из столбцов. Большинство команд в ОС UNIX работают аналогично уже рассмотрен- ным командам cat,pr и wc. Это означает, что они вводят информацию со стандартного ввода, выполняют какие-то действия над этой информа- цией и выводят результаты своей работы на стандартный вывод. Команды такого типа называются фильтрами. На основе фильтров можно строить конвейеры для создания командных строк произвольной сложности. В каждой операционной системе, подобной ОС UNIX, длина конвейера, обычно ограничена сверху, однако было бы странным, если бы Вам потре- бовалось выйти за ее верхний предел (разумеется, не принимая во внима- ние ошибки). Мы уже несколько раз упоминали о таинственном интерпретаторе команд shell, который описывается в sh (1). Интерпретатор команд shell обеспечивает интерфейс пользователей с ОС UNIX на командном уровне. Интерпретатор команд shell является точно такой же командой, как, например, команды Is, echo, рг. Более того, интерпретатор команд shell можно даже сравнить с теми программами, которые написаны Вами. Как Вы уже, наверное, поняли, интерпретатор команд shell обеспечивает интер- претацию команд, переназначение стандартного ввода-вывода и создание конвейеров. Однако эти возможности, предоставляемые интерпретатором команд shell, не исчерпываются. Например, можно поместить в одну командную строку более одной команды не только при построении кон- вейера. Для последовательного выполнения команд Вы можете поместить требуемое число команд в одной командной строке, используя для отде- ления одной команды от другой символ ;. Если во введенной Вами с тер- минала командной строке интерпретатор команд shell обнаруживает один или более символов ;, то он разбивает командную строку на отдельные команды и обеспечивает их последовательное выполнение слева направо. Таким образом, если Вы введете с терминала командную строку команда!; команда2 то команды будут выполнены последовательно, а промптер на терминал будет выведен только после завершения выполнения второй команды. 43
2.10. ВЫПОЛНЕНИЕ КОМАНД В АСИНХРОННОМ РЕЖИМЕ Обычно интерпретатор команд shell ждет завершения выполнения команды и только после этого выводит промптер на терминал. Предполо- жим, что Вы собираетесь выполнить какую-либо команду, выполнение которой занимает много времени (как, например, сортировка большого файла), без потери времени на ожидание завершения выполнения этой команды, а выполняя какие-либо действия во время выполнения этой команды. В таком случае Вы можете ввести с терминала команду в следую- щем виде: sort longfile& Помещение символа & после аргумента команды sort приводит к тому, что интерпретатор команд shell не ожидает завершения выполнения этой команды, а сразу же после вызова ее на выполнение выводит на терминал промптер. Конечно, для предотвращения смешения результатов выполне- ния команды sort с вводимой Вами и выводимой Вам информации жела- тельно ввести с терминала команду sort longfile>sort.ootput& с тем, чтобы переназначить стандартный вывод команды sort с терминала на файл sort.output. Когда команда вызывается на выполнение с использованием символа &, т. е. будет выполняться в асинхронном режиме, то на терминал' выводится число, называемое номером процесса. Это число является уникальным идентификатором команды, выполняющейся в асинхронном режиме. Этот идентификатор может понадобиться позднее для того, чтобы прервать выполнение команды, выполняющейся в асинхронном режиме, поскольку в этом случае специальный символ INTERRUPT не может быть использован для прерывания выполнения команд, выполняющихся в асинхронном режиме. Для того чтобы прервать выполнение команды, выполняющейся в асинхронном режиме, необходимо ввести с терминала команду kill номер—процесса Команда kill посылает специфицированному своим идентификатором процессу специальный сигнал1, в результате получения которого процесс завершается. Командная строка, содержащая несколько команд, отделен- ных друг от друга с помощью символа ;, также может быть вызвана на вы- полнение в асинхронном режиме. Для этого необходимо поместить эту командную строку между символами ( и ) : (команда1; к о манда 2; командап)& 1 Процессы и сигналы будут обсуждены позднее. Сейчас можете ознакомиться с разделами IciUf 1 > и ps(l) в Руководстве. 44
Конвейер также может быть вызван на выполнение в асинхронном режиме: команда1 I команда2& Итак, пока все идет прекрасно; к этому моменту Вы узнали: что такое файл и как файл соотносится с файловой системой; какие средства для защиты файлов имеются в Вашем распоряжении и как ими воспользоваться; как вывести на терминал содержимое каталога; как создать, удалить, скопировать, переименовать файл, вывести содер- жимое файла на терминал; как переназначить стандартный ввод-вывод; как на основе простых команд построить конвейер; как вызвать команду на выполнение в асинхронном режиме. Кроме того, Вы познакомились с рядом команд ОС UNIX, приводимых ниже: cat: объединить файлы cd: сменить текущий каталог chmod: изменить код защиты файла ср: скопировать файл diff: сравнить файлы echo: вывести аргументы kill: прервать выполнение процесса Is: вывести содержимое каталога mail: передать сообщение man: вывести страницу Руководства mkdir: создать каталог mv: переименовать файл рг: вывести содержимое файла pwd: вывести имя текущего каталога rm: удалить файл rmdir: удалить каталог sort: провести сортировку содержимого файла wc: подсчитать число строк, слов и символов в файле Прежде чем перейти к изучению последующего материала, мы предла- гаем начинающим пользователям несколько задержаться в этом месте. Последующий материал является важным, но мы не уверены, что но- вички в состоянии понять его. Они могут предпочесть сначала ознакомиться с редактором текстов ed и, быть может, языком программирования Си, а затем приобрести некоторый опыт использования ОС UNIX. Если Вы перей- дете к чтению следующего параграфа, то Вам, скорее всего, придется на не- которое время прекратить использовать ОС UNIX для того, чтобы "пере- варить" полученную информацию, зато впоследствии это окажет Вам неоце- нимую помощь. 45
ЯЗЫК ПРОГРАММИРОВАНИЯ shell 2.11. МЕТАСИМВОЛЫ И ГЕНЕРАЦИЯ ИМЕН ФАЙЛОВ В большинстве команд ОС UNIX в качестве аргументов используются имена файлов. Имена файлов могут быть как собственно именами файлов в текущем каталоге, так и полными именами файлов (абсолютными или относительными), расположенными в произвольных каталогах. Собственно имена файлов обычно состоят из последовательности строчных и прописных букв латинского алфавита, цифр и, быть может, одного или более симво- лов . . В действительности в собственно именах файлов возможно употреб- ление любых символов, кроме, конечно, символа /. Следует избегать ис- пользования в именах файлов символов, отличных от строчных и пропис- ных букв латинского алфавита и цифр, так как многие из этих символов имеют специальный смысл для интерпретатора команд shell. Они называют- ся метасимволами. С некоторыми из метасимволов Вы уже познакомились, например с метасимволами &, <, > и ». Символ . — исключение (он не является метасимволом). Этот символ используется для выделения общих частей в именах файлов. По принятому соглашению часть имени файла, следующая за символом ., специфицирует характер содержимого файла. Например, файлы prog prog.c prog.1st prog.o prog.out prog .s должны быть логически связаны друг с другом. Чтобы подчеркнуть логи- ческую связь этих файлов друг с другом, в их именах используется общая основа или, иначе говоря, общий корень. Файл prog.c должен содержать исходный текст программы на языке программирования Си, файл prog.o — объектный код этой же программы, файл prog — код выполняемой прог- раммы и т. д. Эти соглашения используются в целом ряде команд ОС UNIX, но они никак не связаны с самой операционной системой, т. е. не диктуются ею. В интерпретаторе команд shell предусмотрен простой механизм генера- ции имен файлов. Полные имена файлов, как абсолютные, так и относитель- ные, могут быть записаны с использованием метасимволов, заменяющих часть имени файла или даже все имя файла целиком. Выражения, в которые входят метасимволы, называются метанотациями. Метанотация prog* является обозначением всех приведенных выше имен файлов. Чтобы прове- рить это, можно воспользоваться для вывода на терминал вышеперечис- ленных имен файлов (если, конечно, файлы с этими именами имеются в текущем каталоге) командой echo prog* Строка, содержащая имена всех этих файлов, была выведена на терминал с помощью этой команды. Метанотация prog* перед выполнением команды echo будет расширена интерпретатором команд shell. При этом осуще- 46
ствляется сортировка всех соответствующих данной метанотации имен файлов в алфавитном порядке, а затем каждое из таких имен файлов передается команде echo в виде отдельного аргумента. Метасимвол « соответствует любой, в том числе и пустой, последова- тельности символов в имени файла. Использование собственно метасимвола * в качестве аргумента команды приводит к тому, что в качестве аргумен- тов этой команды будут использованы имена всех файлов, содержащихся в текущем каталоге, предварительно отсортированные в алфавитном порядке, кроме имен файлов, начинающихся с символа.. Метасимвол ? соответствует произвольному символу в имени файла. Таким образом, команда 1$ ? ?? ??? выводит на терминал имена всех файлов, содержащихся в текущем катало- ге и состоящих из одного, двух и трех символов. Последовательность сим- волов, заключенная между символами [ и ], соответствует любому симво- лу из этой последовательности1. Таким образом, метанотация prog.[cos] соответствует именам файлов ргод.с, ргод.о и prog.s из приведенного выше примера. Вы должны хорошо запомнить три вещи: Все расширения имен файлов (вследствие использования метанота- ций) производятся интерпретатором команд shell до начала выполнения команды; Каждое имя файла, соответствующее использованной метанотации, передается команде как отдельный аргумент; Если использованной метанотации не соответствует никакое имя файла, то метанотация передается команде в качестве аргумента целиком в том виде, в каком она была записана (со всеми входящими в нее метасимво- лами) . Использование метасимволов для генерации имен файлов, так же, впро- чем, как и метасимволов, обеспечивающих переназначение стандартного ввода-вывода и вызов команд на выполнение в асинхронном режиме, приводит к тому, что метасимволы непосредственно не могут быть исполь- зованы в качестве составных частей аргументов команд. Одним из способов использования метасимвола в качестве составной части аргумента команды является помещение всего аргумента, содержащего любые метасимволы (кроме символа ') между символами ' и '. Таким образом, если Вы хотите вывести на терминал символ *, то можете ввести с терминала команду echo ’ Внутри квадратных скобок возможно использование символа — для того, чтобы избежать перечисления всех символов, если символы упорядочены лексикографи- чески. Например, метанотация [А — Z, а — z] соответствует любой букве латинского алфавита. {Прим. ред.) 47
Другим способом использования метасимволов в качестве составных частей аргументов команд является употребление символа \ непосред- ственно перед каждым имеющимся в аргументе команды метасимволом. Сам символ \ также является метасимволом. Использование метасимвола \ приводит к тому, что следующий непосредственно за ним символ не будет рассматриваться интерпретатором команд shell как метасимвол. Это назы- вается экранированием метасимвола. В качестве примера рассмотрим команду Is —I xyyz \\f\1\* В результате выполнения этой команды на терминал будет выведена ин- формация о файле xyyz\fI*, содержащемся в текущем каталоге. Если Вы всю жизнь мечтали о возможности использования в качестве имен файлов комбинаций символов такого типа, то мы рады сообщить Вам, что ОС UNIX позволяет делать это. Безусловно, возможности, предоставляемые Вам интерпретатором команд shell, по генерации имен файлов, использованию метасимволов и экранированию последних не ограничиваются приведенными в описанных выше примерах. Более полное описание всех возможностей интерпрета- тора команд shell приведено в приложении В. Прежде чем перейти к изуче- нию материала следующего параграфа, рекомендуем Вам поупражняться в использовании тех возможностей интерпретатора команд shell, с которы- ми Вы только что познакомились. 2.12. ПЕРЕМЕННЫЕ В ЯЗЫКЕ ПРОГРАММИРОВАНИЯ shell Интерпретатор команд shell обеспечивает возможность использования переменных типа "строка символов". Имена переменных могут быть выбраны пользователем. Пользователь имеет возможность присвоить переменной значение некоторой строки символов. Например, команда mark = /usr/andy/bin присваивает значение строки символов /usr/andy/bin переменной mark типа "строка символов". Значение, присвоенное некоторой переменной, может быть впоследствии использовано. Для этого в соответствующем месте командной строки должно быть употреблено имя этой переменной, которому предшествует метасимвол $. Например, команда mv afile $mark переместит файл afile из текущего каталога в каталог с абсолютным пол- ным именем /usr/andy/bin. Использование значения, присвоенного некото- рой переменной, называется подстановкой. Для того чтобы имя переменной не сливалось с символами, которые могут следовать за ним в командной строке, при подстановке в общем случае используется следующая форма записи: ${имя переменной} 48
например, использование команд b = /tmp/andy- Is -1 myfile > ${b}ls приведет к переназначению стандартного вывода команды 1s с терминала на файл /tmp/andy— Is, а использование команды Is —I >$bls приведет к подстановке в командную строку значения переменной bls. Если переменной bls не было предварительно присвоено никакого значения, то ее значением является символ i__i. Имена некоторых переменных имеют для интерпретатора команд shell специальный смысл. Значением переменной PATH (т. е. $РАТН) является список каталогов, в кйторых интерпретатор команд shell осуществляет поиск файла, содержащего программу, обеспечивающую выполнение указанной в командной строке команды, в том случае, если указанное имя файла не содержит ни одного символа /. Если Вы сами явно не при- своите переменной PATH какого-либо значения, то стандартной (по умол- чанию) последовательностью поиска файла является следующая: текущий каталог, каталог /Ып, каталог /usr/bin. Именно в такой последовательности интерпретатор команд shell отыскивает файлы, содержащие программы, которые обеспечивают выполнение таких, например, команд, как echo, Is и cat. В списке каталогов, являющемся значением переменной PATH, имена каталогов отделяются друг от друга с помощью символа :. В ка- честве примера рассмотрим команду PATH = : /usr/andy/bin:/usr/mike/bin:/bin;/usr/bin После выполнения этой команды поиск файла, имя которого не содержит ни одного символа /, будет проводиться в следующей последовательности: текущий каталог (пустая строка символов перед первым символом :), каталог /usr/andy/bin, каталог /usr/mike/bin, каталог /Ып и, наконец, каталог /usr/bin. Таким образом, пользователи имеют возможность созда- вать собственные команды, помещать эти команды в свои каталоги и осу- ществлять независимый доступ к этим командам. Для некоторого ускоре- ния поиска пользователь может скопировать наиболее часто используемые им команды в какой-либо из своих каталогов, например в каталог, имею- щий в его основном каталоге пользователя имя bin. Если имя команды содержит хотя бы один символ /, то последовательность поиска, предписы- ваемая значением переменной PATH, нарушается. В этом случае в зависи- мости от того, является имя команды абсолютным или относительным, поиск начинается соответственно от корневого каталога или текущего каталога. 49
Как Вы помните, когда интерпретатор команд shell готов к приему следующей командной строки, то он выводит на терминал промптер. По умолчанию промптером является символ $, однако Вы можете изменять промптер по своему усмотрению, присваивая соответствующие значения переменной PS1. Например, команда PS1 = 'hello ’ производит смену промптера интерпретатора команд shell. После выполне- ния этой команды интерпретатор команд shell в качестве промптера будет использовать строку символов 'hello'. Вообще говоря, использование промптера, завершающегося по крайней мере одним пробелом, очень удоб- но, так как не позволяет вводимому с терминала тексту слиться с промп- тером. В некоторых случаях команды могут выполняться в интерактивном режиме, т. е. в ходе выполнения команд имеет место диалог с пользова- телем. Такие команды используют свой собственный промптер, обычно отличающийся от промптера интерпретатора команд shell. По умолчанию такие команды используют в качестве промптера символ >. Вы можете изменить и этот промптер, присваивая соответствующее значение перемен- ной PS2, как, например, это делается в команде PS2 = 'again ' Проверить значение всех переменных, используемых интерпретатором команд shell, можно с помощью команды set следующим образом: * set IFS = PATH = :/usr/andy/bin;/bin:/usr/bin:/usr/games PS1 =1 PS2- > TERM = 920c $ Значение переменной TERM определяет тип используемого терминала. Эта информация передается1 выполняемым командам для определения характеристик используемого терминала. Примером команды, которой необходима такая информация, является экранный редактор текстов. Другими переменными, имеющими для интерпретатора команд shell спе- циальный смысл, являются следующие переменные: 1 Для передачи этой информации выполняющейся программе используется сис- темный вызов. Набор передаваемых из интерпретатора команд shell в выполняю- щуюся программу переменных называется окружением. См. гл. 8, разд. 1 и разд. 2 Руководства. 50
$? $# $$ $! $- SHOME $MAIL $IFS Вы должны избегать их использования. Более подробные объяснения на этот счет можно найти в приложении В. 2.13. КОМАНДНЫЕ ФАЙЛЫ И ПРОЦЕДУРЫ НА ЯЗЫКЕ ПРОГРАММИРОВАНИЯ shell Подобно тому как переназначается стандартный ввод команде sort, Вы можете переназначить стандартный ввод и самому интерпретатору команд shell, с тем чтобы последний вводил команды, которые Вы хотите выпол- нить, из некоторого файла. Поясним это на примере. Предположим, что каждый раз при входе в ОС UNIX Вы хотите знать текущие дату и время, а также, кто из пользователей работает в данный момент в ОС UNIX вместе с Вами. Команда date обеспечивает вывод текущих даты и времени на стандартный вывод, т. е. на терминал: $ date Fri Jan 14 16:51:56 GMT 1982 $ Команда who обеспечивает вывод на стандартный вывод имени, под кото- рым пользователь зарегистрирован в ОС UNIX, имени терминала и времени входа в ОС UNIX для каждого из пользователей, работающих в данный момент в ОС UNIX: $ who jackie ttyr Jan 14 16:13 andy ttyG Jan 14 09:12 mike ttym Jan 14 12:35 $ В этом случае одним из способов получения необходимой информации является последовательное использование команд date и who сразу же после Вашего входа в ОС UNIX. Однако намного проще воспользоваться тем, что интерпретатор команд shell имеет возможность вводить команды из файла после переназначения его стандартного ввода на этот файл. Снача- ла создадим файл, содержащий необходимые команды, и назовем этот файл startup. Это можно сделать с помощью команды echo 'date; who'> startup Эта команда помещает командную строку, состояющую из двух команд date и who, разделенных символом ;, в файл startup. Использование символов ' и ', между которыми помещен аргумент команды echo, здесь необходимо, поскольку в противном случае интерпретатор команд shell 51
распознав символ ;, разобьет исходную командную строку на две коман- ды: команду echoc аргументом date и команду who, стандартный вывод которой предназначен с терминала на файл startup. Файл, содержащий команды, называется командным файлом, а его со- держимое — процедурой на языке программирования shell. (На прак- тике эти два термина часто употребляются как эквивалентные). Выпол- нить этот командный файл можно с помощью любой из следующих команд: sh < startup или sh startup Обе эти команды приводят к вызову на выполнение интерпретатора команд shell, у которого стандартный ввод переназначен с терминала на файл startup. Результаты выполнения этих двух команд будут одинаковы; они совпадают с результатами последовательного ввода с терминала команд, содержащихся в файле startup. Еще лучшим решением этой задачи является автоматический вызов на выполнение командного файла, содержащего необходимые команды, немедленно после Вашего входа в ОС UNIX. Следует отметить, что ОС UNIX предоставляет Вам такую возможность. Для этого достаточно, исполь- зуя команду mv, переместить файл startup в Ваш основной каталог поль- зователя с одновременным переименованием его в файл .profile1. Тогда интерпретатор команд shell сразу после Вашего входа в ОС UNIX, прежде чем вывести на терминал промптер, сообщающий о его готовности к прие- му Ваших команд, выполнит команды, содержащиеся в файле .profile. Командная строка, которая была использована ранее для объединения и вывода на терминал четырех файлов: cat datl dat2 dat3 dat4 I pr —h Test—Data теперь может быть помещена в командный файл и еще больше упрощена. Для этого создадим файл prdata, содержащий следующий конвейер: cat dat? k pr —h Test—Data Выполнить этот командный файл можно, введя с терминала команду sh prdata Метанотация dat? соответствует именам файлов, состоящим из четырех символов и начинающимся с последевательности символов dat. Если Вы предполагаете часто использовать некоторый командный файл, то было бы желательно избавиться от необходимости вводить последовательность символов sh каждый раз при вызове этого командного файла на выпол- нение. Операционная система UNIX позволяет Вам и это. Для того чтобы при вызове командного файла на выполнение не вводить каждый раз 1 Файл .profile используется стандартным интерпретатором команд shell. В Вашей версии ОС UNIX для тех же целей может применяться файл с каким-либо другим именем. См. в Руководстве sh (1). 52
последовательности символов sh, необходимо изменить код защиты этого командного файла, обеспечив доступ к этому файлу по выполнению. Это может быть сделано с помощью команды chmod + х prdata Теперь Вы можете вызывать свой командный файл на выполнение просто, вводя его имя с терминала так, как будто'он является выполняемой про- граммой. Интерпретатор команд shell распознает, что в Вашем файле на самом деле хранится не выполняемая программа, а программа, написан- ная на языке программирования shell, и осуществит ее интерпретацию. 2.13.1. ПЕРЕДАЧА ПАРАМЕТРОВ В КОМАНДНЫЕ ФАЙЛЫ При вызове командного файла на выполнение параметры ему могут быть переданы точно таким же образом, как и выполняемой программе. С точки зрения командного файла эти параметры являются позиционными. Символ $ является, как Вы, наверное, помните, метасимволом интерпре- татора команд shell. Он используется, в частности, для ссылки на пара- метры, точнее, для получения их значений в командном файле. В команд- ный файл можно передать до девяти параметров. При использовании где- либо в командном файле комбинации символов $i, где 1 < i О, вместо нее будет осуществлена подстановка значения параметра с порядковым номером i, т. е. аргумента командного файла с порядковым номером i. Использование комбинации символов $0 приводит к подстановке вместо нее имени данного командного файла. Рассмотрим это на примере. Пусть к командному файлу where имеется доступ по выполнению и этот команд- ный файл содержит следующий конвейер: who I grep $1 Если Вы введете с терминала команду where andy то в случае, если пользователь, зарегистрированный в ОС UNIX под име- нем andy, в данный момент работает в ОС UNIX, на терминал будет выведе- на строка, содержащая номер терминала, используемого указанным поль- зователем. Если же в данный момент этот пользователь не работает в ОС UNIX, то на терминал не будет выведено ничего. Команда grep производит контекстный поиск в тексте, поступающем со стандартного ввода, для нахождения в этом тексте строк, содержащих последовательности символов, переданные ей в качестве аргументов, и выводит результаты своей работы на стандартный вывод. В этом примере команда grep используется как фильтр, обеспечивающий ввод со стан- дартного ввода и вывод всех строк, содержащих последовательность сим- волов andy, на стандартный вывод. Более подробно команда grep рассмат- ривается в гл. 6. В ходе интерпретации этого командного файла интерпретатором команд shell вместо комбинации символов $1, осуществляется подстановка зна- 53
чения первого и единственного параметра andy. Если предположить, что пользователь, зарегистрированный в ОС UNIX под именем andy, в данный момент работает в ОС UNIX, то на терминале Вы увидите примерно сле- дующее: $ where andy andy ttyG Jan 14 09:12 $ Возможности команды grep значительно больше, чем те возможности, которые были использованы нами в приведенном выше примере (см. дгер(1)). При использовании в командном файле комбинации символов $# вместо нее будет осуществлена подстановка числа параметров, указанных в командной строке при вызове данного командного файла на выполнение. Давайте создадим файл howmany и поместим в этот файл команду echo $# Теперь, изменив код защиты этого файла с тем, чтобы обеспечить к нему доступ по выполнению, попробуем выполнить этот командный файл с не- которым числом параметров: $ howmany arguments were used 3 $ Попробуйте поупражняться в использовании командных файлов, подобных командному файлу howmany. Если у Вас что-то не получилось, то попытай- тесь объяснить, почему? Не забывайте о том, что символ $ является мета- символом интерпретатора команд shell. При вызове на выполнение командного файла, в котором используется комбинация символов $*, вместо нее будет произведена подстановка всех аргументов, указанных в командной строке, за исключением имени этого командного файла, которому соответствует комбинация символов $0. Типичная ситуация, при которой необходимо использование комбинации символов $*, возникает, когда одни и те же действия необходимо произ- вести над всеми параметрами командного файла. Рассмотрим, например, командный файл, содержащий командную строку sort —nr $* Этот командный файл обеспечивает сортировку в порядке убывания содер- жимого всех файлов, имена которых будут указаны в качестве его аргумен- тов. Преимущество использования комбинации символов $* вместо пере- числения комбинаций символов $1, . . . , $9 заключается в том, что в этом случае имеется возможность передать в командный файл более девяти параметров. Командные строки, начинающиеся с символа :, полностью игнорируются интерпретатором команд shell. Используя строки, начинающиеся с симво- 54
ла Вы можете вставлять в командные файлы комментарии. Это очень полезно. Проиллюстрируем сказанное следующим примером: : the command 'where'1 : a single line is printed showing the terminal1 2 3 : if the username cannot be found then nothing is printed1 who | grep $1 2.14. УПРАВЛЕНИЕ ПОСЛЕДОВАТЕЛЬНОСТЬЮ ДЕЙСТВИЙ В ЯЗЫКЕ ПРОГРАММИ- РОВАНИЯ shell Часто бывает необходимо обеспечить проведение каких-либо действий циклически и управление дальнейшими действиями в зависимости от ре- зультатов проверки некоторого условия. Для решения подобных задач язык программирования shell предоставляет Вам возможность исполь- зовать такие управляющие конструкции, как for, case, if и while. С точ- ки зрения интерпретатора команд shell эти управляющие конструкции являются обычными командами и могут использоваться как при соз- дании командных файлов, так и при работе в интерактивном режиме. Команды, реализующие подобные конструкции, по сути дела являются операторами языка программирования shell. Поэтому при описании язы- ка программирования shell термин "оператор" будет использоваться на- равне с термином "команда". Термин "оператор", используемый в этой главе, нуждается, вообще говоря, в строгом определении, которое Вы сможете найти в литературе из приведенного нами списка. В случае исполь- зования управляющих конструкций в интерактивном режиме после ввода с терминала первой командной строки промптер интерпретатора команд shell изменяется на S>PS2, т. е. на значение переменной PS2. Команды ОС UNIX возвращают код завершения, значение которого может быть использовано для принятия решения о дальнейших действиях. Команда test, например, создана специально для использования в команд- ных файлах. Единственная функция этой команды заключается в выработ- ке кода завершения. Так, например, команда test —f file возвращает нулевой код завершения ("истина"), если файл file существует, и ненулевой код завершения ("ложь") в противном случае. Подробно команда test описана в test (1), а здесь приведено лишь несколько наибо- лее часто используемых флагов этой команды: 1 Команда'where’. (Прим, пер.} 1 Выводит одну строку, содержащую имя терминала. (Прим, пер.} 3 Если ими пользователя не найдено, то не выводит ничего, (Прим, пер.] 55
test s — истина, если аргумент s имеет значение "истина"; test —f file — истина, если файл file существует; test —г file — истина, если файл file доступен по чтению; test —w file — истина, если файл file доступен по записи; test —d file — истина, если файл file является каталогом. 2.14.1. ОПЕРАТОР ЦИКЛА for В обобщенной форме оператор цикла for выглядит следующим обра- зом: for имя [ in список-значений] do список—команд done При каждом следующем выполнении оператора цикла for переменная "имя" принимает следующее значение из списка значений, задаваемых списком "список—значений". Вообще говоря, "список—значений" являет- ся необязательным. При его отсутствии оператор цикла for выполняется для всех позиционных параметров или, иначе говоря, аргументов. Таким образом,оператор for I эквивалентен оператору for I in $*. Выполнение оператора цикла for завершается, когда "список—значений" будет исчер- пан. Последовательность команд (операторов), задаваемая списком "спи- сок—команд", состоит из одной или более команд интерпретатора команд shell, отделенных друг от друга с помощью символов newline или ;. Рассмотрим примеры использования оператора цикла for. В результате выполнения оператора for A in alpha beta gamma do echo A done на терминал будет выведено следующее: alpha beta gamma Предположим, что Вы хотите найти во всех файлах текущего каталога, содержащих исходные тексты программ, написанных на языке програм- мирования Си, все вхождения функции с некоторым именем. Это можно сделать с помощью такой последовательности команд: for i do grep 3>i »,c done Поместив эту последовательность команд в файл findref, Вы сможете затем, используя команду findref 'hash (' 'insert (' 'symbol (' 56
вывести на терминал все строки из всех файлов текущего каталога, имена которых оканчиваются символами .с, содержащие ссылки на функции hash (), insert () и symbol (). Использование символов ' в вышепри- веденном примере необходимо для снятия специального смысла с сим- вола (. 2.14,2. ОПЕРАТОР ВЫБОРА case Оператор выбора case реализует возможность ветвления на произволь- ное число ветвей, которая обеспечивается в большинстве современных язы- ков программирования, предполагающих использование структурного под- хода. В обобщенной форме оператор выбора case выглядит следующим образом: case имя in шаблон) список—команд;; esac Выполнение оператора выбора case сводится к тому, что выполняется последовательность команд (операторов), задаваемая списком "список— команд", в строке, для которой значение переменной "имя" совпадает с шаблоном "шаблон"1. Поскольку метасимвол * соответствует произ- вольной, в том числе и пустой, последовательности символов, то, исполь- зуя его в качестве шаблона "шаблон" в строке, непосредственно пред- шествующей строке, содержащей служебное слово esac, можно реализо- вать действия, которые необходимо произвести в случае, если значение переменной "имя" не совпадает ни с одним из шаблонов, заданных в виде "шаблон" в предшествующих строках2. Рассмотрим примеры использования оператора выбора case. В резуль- тате выполнения оператора for A in alpha beta gamma do case $A in alpha) B = a; beta) В = c; gamma) В = e; esac echo $B done на терминал будет выведено следующее: а с е 1 Здесь имеется в виду первое встреченное совпадение. [Прим. ред.) 5 В некотором смысле это аналог default для оператора switch, имеющегося в языке программирования Си. (Прим. ред.) 57
Попробуйте ввести с терминала этот оператор, Промптер интерпретатора команд shell после ввода первой строки изменится на $PS2 (обычно это символ >). Оператор выбора case удобно использовать для выделения различных аргументов. Приведенный ниже пример иллюстрирует эту воз- можность (этот пример представляет собой упрощенную версию или, иначе говоря, прототип команды compile) : initialise flag to nothing1 f lag = repeat for each argument1 2 * for a in I- do case la in concatenate the flags with a separating space1 - [ocSO]) flag = $flag' 'la;; - •) echo 'unknown flag la';; compile each source file then reset flag4 * .c) cc Iflag la; flag = ;; * .s) as Iflag la; flag = ;; - ,f) f77 Iflag la; flag = ;; complain if a bad argument given5 ) echo ‘unexpected argument la';; esac done Вы должны запомнить, что в операторе выбора case каждая строка, кроме первой и последней, завершается символами а весь оператор выбора case в целом завершается служебным сливом esac, т. е. case — наоборот, 2,14.3, УСЛОВНЫЙ ОПЕРАТОР if В обобщенной форме условный оператор if выглядит следующим обра- зом6 : if список —команд then список — команд [else список — команд! fi 1 Инициализировать флаг, {Прим, пер.) 5 Повторять для каждого аргумента. (Прим, пер.) ’ Объединить флаги, разделив их пробелами. {Прим, пер.) 4 Транслировать каждый файл, содержащий исходный текст, а затем сбросить флаг. {Прим, пер) s Вывести сообщения в случае неверного аргумента. {Прим, пер) * Приведенная здесь форма условного оператора if не является обобщенной. См. приложение В. {Прим, ред.) 58
Выполнение условного оператора if сводится к тому, что сначала выпол- няется последовательность команд (операторов), которую задает "список— команд" в строке, содержащей служебное слово if. Затем, если последняя выполненная команда из этой последовательности команд возвращает нулевой код завершения ("истина"), будет выполнена последовательность команд (операторов), которую задает "список—команд" в строке, содер- жащей служебное слово then. Строка, содержащая служебное слово else, является необязательной. Если она присутствует, то последовательность команд (операторов), которую задает "список—команд" в строке, содер- жащей служебное слово else, будет выполнена только при условии, что последняя выполненная команда из последовательности команд (операто- ров) , которую задает "список—команд" в строке, содержащей служебное слово if, возвращает ненулевой код завершения ("ложь"). Рассмотрим следующий пример: for A in * do if test - d $A then echo $A: is a directory else echo - n $A: is a file and if test - w $A then echo writeable else if test - r $A then echo readable else echo neither readable nor writeable fi fi fi done Первая строка в приведенном выше примере обеспечивает выполнение всех последующих действий в цикле для всех имен файлов из текущего каталога, при этом переменная А на каждом шаге последовательно прини- мает значения, равные именам этих файлов. Первая содержащая служебное слово if строка проверяет, является ли файл, имя которого представляет собой текущее значение переменной А, каталогом. Если этот файл является каталогом, то на стандартный вывод выводятся имя этого файла и сообще- ние о том, что файл с указанным именем является каталогом. Эти действия в приведенном выше примере обеспечиваются в результате выполнения третьей строки. Оставшиеся строки выполняются только в том случае, если проверка того, является ли файл, имя которого представляет собой текущее значение переменной А, каталогом, дает отрицательный ответ. Это означает, что файл, имя которого представляет собой текущее значение переменной А, 59
является обычным файлом1. Если этот файл является обычным файлом, то на стандартный вывод выводятся имя этого файла и сообщение о том, что файл с указанным именем является обычным файлом. Эти действия в приведенном выше примере обеспечиваются в результате выполнения четвертой строки. Особенностью использования команды echo в этой строке является использование флага — п, благодаря чему выводимая командой echo строка не будет дополнена символом newline, что позво- ляет впоследствии дополнить эту строку, как это, например, показано в приведенном выше примере. Вторая, содержащая служебное слово if, строка проверяет, доступен ли по записи файл, имя которого представляет собой текущее значение переменной А. Если этот файл доступен по записи, то строка дополняется соответствующим сообщением. Если же этот файл недоступен по записи, то проверяется, доступен ли этот файл по чтению. Эти действия в приведенном выше примере обеспечиваются в результате выполнения седьмой строки. Если этот файл доступен по чтению, то строка дополняется соответствующим сообщением. Если же этот файл недоступен ни по записи, ни по чтению, то строка также дополняется соответствующим сообщением. Эти действия в приведенном выше примере обеспечиваются в результате выполнения девятой строки. 2.14.4. ОПЕРАТОРЫ цикла while и until В обобщенной форме оператор цикла while выглядит следующим обра- зом: while список—команд do список—команд done Выполнение оператора цикла while сводится к тому, что сначала выпол- няется последовательность команд (операторов), которую задает "список— команд" в строке, содержащей служебное слово while, а затем, если последняя выполненная команда из этой последовательности команд возвращает нулевой код завершения ("истина"), выполняется последо- вательность команд (операторов), которую задает "список—команд" в строке, содержащей служебное слово do, после чего осуществляется без- условный переход на начало оператора цикла while. Выход из цикла будет осуществлен тогда, когда последняя выполненная команда из после- довательности команд (операторов), которую задает "список—команд" в строке, содержащей служебное слово while, возвратит ненулевой код завершения ("ложь"). Приведенный ниже фрагмент командного файла иллюстрирует использование оператора цикла while. В нем реализуется ожидание события, состоящего в удалении файла с определенным именем. 1 На самом деле, не будучи каталогом, файл может быть как обычным файлом, так и специальным файлом. (Прим, ред.) 60
и только после наступления этого события производятся дальнейшие дей- ствия. Механизм, продемонстрированный в приведенном примере, по сути дела является iпростой реализацией механизма синхронизации взаимодей- ствующих процессов на основе семафоров1. while test -f lockfile do sleep 30 echo waiting for semaphore done : create the semaphore file2 echo > lockfile : further commands3 При замене в операторе цикла while служебного слова while на until ус- ловие, при выполнении которого осуществляется выход из цикла, меняется на противоположное. В остальном оператор цикла while и оператор цикла until идентичны. В обобщенной форме оператор цикла until выглядит следующим образом: until список—команд do список—команд done Следующие две команды ОС UNIX,рассмотренные ниже, используются только совместно с управляющими конструкциями языка программиро- вания shell: это команда true, которая всегда возвращает код завершения, равный нулю (т. е. "истина"), и команда false, которая всегда возвращает код завершения, не равный нулю (т. е. "ложь"). Ниже приведены два при- мера, иллюстрирующие бесконечные циклы, которые будут выполняться до тех пор, пока ЭВМ не сломается или не будет выключена (ну, по крайней мере, до тех пор, пока Вы не нажмете клавишу, соответствующую специаль- ному символу INTERRUPT) : while true do echo hello andy done 1 Концепция семафоров была введена Э. В. Дийкстрой (1965). Семафор можно трактовать как флаг, установленный или сброшенный для того, чтобы запретить или разрешить доступ к общим объектам. В простых системах в качестве семафоров используют файлы; наличие файла означает запрет доступа к общему объекту. Однако файл, строго говоря, не является семафором. См. [ 1 ], с. 16—23 и [ 2]. г Создать файл-семафор. (Прим, пер.) 1 Последующие команды. (Прим, пер.) 61
и until false do echo hello mike done 2.15. ЗАКЛЮЧЕНИЕ Итак, Вы познакомились с азами работы с файлами. Затратив сейчас некоторое время на приобретение практического опыта работы с файлами. Вы сэкономите впоследствии массу времени. В этой главе вкратце были описаны возможности, предоставляемые Вам интерпретатором команд shell. Однако, несмотря на краткость этого описания, его вполне доста- точно для того, чтобы начать работать. По мере освоения возможностей, предоставляемых Вам ОС UNIX, Вы сможете использовать их более полно. В качестве примера командного файла мы включили в эту книгу вариант команды man. Если Вы уже использовали команду man, то у Вас могло создаться ошибочное впечатление, что эта команда реализована в виде выполняемой программы. По-видимому, вариант команды man, исполь- зуемый в Вашей версии ОС UNIX, будет очень похож на тот, что исполь- зуется у нас. Для того чтобы проверить это, воспользуйтесь командой cat /bin/man или cat /usr/bin/man Получив на терминале вариант команды man, используемый в Вашей версии ОС UNIX, сравните его с тем вариантом команды man, который исполь- зуется у нас: cd /usr/man : 'default nroff ($N), section 1 (Ss)'1 N = n s = 1 for i do case $i in [1-9]) s = $i;; - t) N = t;; - n) N = n;; - •) echo unknown flag • ) if test -f man$s/$i.$s then ${N}roff manO/${N}aa man$s/$i.$s else 1 По умолчанию — c. s, разд. 1 Руководства, сформатированная с помощью команды nroff. (Прим, пер.) 62
: look through all manual sections1 fou nd = no for j in 1 2 3 4 5 6 7 8 9 do if test -f man$]/$i.$j then man $i found = yes fi done case Ifound in no) echo "$i: manual page not found" esac fi esac done 2.16. УПРАЖНЕНИЯ 1. Какова максимальная длина имени файла? 2. Что такое основной каталог пользователя? 3. Чем ограничивается число Ваших каталогов и файлов? 4. Каким образом Вы можете выяснить, является файл каталогом или обычным файлом? 5. Каков механизм защиты файлов в ОС UNIX? 6. Что такое группа? 7, Куда по умолчанию назначен стандартный ввод-вывод? 8. Что такое метасимволы? Какое отношение метасимволы имеют к генерации имен файлов? 9. Каким образом Вы можете замкнуть стандартный вывод одной команды на стан- дартный ввод другой команды, явно не создавая при этом файла? Нужно ли при этом подвергнуть команды каким-либо изменениям? ГЛАВА 3. РЕДАКТОР ТЕКСТОВ ОС UNIX Томик стихов, вино и хлеб — под дере- вом редактор и ты. (Да простит нам Фитцджеральд). Оставим пока еду и выпивку и займемся редактором текстов. 3.1. ПРЕДВАРИТЕЛЬНЫЕ ЗАМЕЧАНИЯ Если Вы - рядовой пользователь ОС UNIX, то большую часть Вашего времени займет работа с редактором текстов ed. Редактор текстов ed и интерпретатор команд shell-две наиболее важные для Вас компоненты 1 Просмотреть все разделы руководства. (Прим, пер.} 63
ОС UNIX. Вы должны как следует изучить их, потому что будете обращать- ся к ним очень часто, используя интерпретатор команд shell для выполне- ния программ, подготовленных с помощью редактора текстов ed. Если Вы задумаетесь о редактировании текстов вообще, то обнаружите специфическую особенность работы программистов. Программисты, использующие интерактивные операционные системы, тратят, по всей ве- роятности, большую часть своего рабочего времени на работу с редактором текстов, а не на использование каких-либо других компонент програм- много обеспечения. Подготовка программ, документации и тестовых данных, оформление результатов работы — все это выполняется с помощью редактора текстов. Споры о том, какой из языков программирования лучше: Бейсик, Пас- каль или Фортран — практически нескончаемы, в то время как вопрос о редакторах текста не поднимается вовсе. Если Вы внимательно просмот- рите литературу, то найдете столь малое число публикаций по этой теме, что невозможно сравнивать ее с такой представительной областью, как разработка языков программирования. Международные комитеты тратят огромные суммы денег на стандартизацию вновь созданного языка прог- раммирования и его развитие. И в то же самое время сотни программистов во всем мире продолжают изобретать редакторы текстов, которые не обла- дают никакой новизной, кроме полной несовместимости со своими пред- шественниками. Мы считаем, что ОС UNIX — это хорошо спроектированная и вполне работоспособная операционная система, однако пользователи не прекра- щают попыток модернизировать ее. Некоторые поставщики операционных систем, подобных ОС UNIX, не извлекшие никакого опыта из печальной истории Бейсика и Фортрана, считают необходимым включать собствен- ный "усиленный" редактор текстов в продаваемые ими операционные систем1,1. Судите сами, разумно ли они поступают. При использовании такого нестандартного редактора текстов, имеюще- гося в купленной Вами операционной системе, обратите, прежде всего, особое внимание на его мобильность. Даже если бы ОС UNIX не была хорошей операционной системой, ее высокая мобильность быстро сделала бы ее популярной1. К сожалению, очень часто разработчики программного обеспечения не принимают, во внимание возможность смены аппаратных средств. Если Вы начнете использовать нестандартное программное обеспе- чение, не имея его исходных текстов, то неизбежно столкнетесь с большими трудностями при попытке осуществить его перенос с ЭВМ одного типа на ЭВМ другого типа. Пользователи, применяющие лишь немобильный редактор текстов (под мобильным мы понимаем то, что перенесено, а не 1 По нашему твердому убеждению, никакая (даже уникальная) мобильность опера- ционной системы не может компенсировать ее серьезных недостатков. (Прим, ред.) 64
то, что по утверждению поставщика может быть перенесено}, оказываются при этом в очень трудном положении. 3.2. РЕДАКТОР ТЕКСТОВ ОС UNIX В стандартной версии 7 ОС UNIX имеется только один редактор текстов — редактор текстов ed. Вопрос о том, насколько он хорош или плох, неуместен по двум причинам: во-первых, редактор текстов ed моби- лен и вполне работоспособен, а во-вторых, другого редактора текстов в стандартной версии 7 ОС UNIX все равно нет, и, следовательно, выбирая в качестве операционной системы ОС UNIX, Вы неизбежно выбираете в качестве редактора текстов редактор текстов ed. Таким образом, все, что Вам остается, — это попробовать его в работе. Причины, приковавшие наше внимание к редактору текстов ed, должны теперь быть очевидны! Трудно было бы описать здесь редактор текстов ed лучше, чем это сделано в Руководстве, поэтому, если это возможно, обратитесь к нему [3, 4]. Еще лучше воспользоваться услугами обучающей системы learn, входящей в состав стандартной версии 7 ОС UNIX1. Для этого необходимо прежде всего войти в ОС UN IX, а затем ввести с терминала learn ed и следовать указаниям обучающей системы learn. Если Вы лишены обеих этих возможностей или хотите иметь собственный карманный справочник по использованию редактора текстов ed, то воспользуйтесь материалами, имеющимися в рамках данной книги: введение в использование редактора текстов ed подробный аннотированный перечень особенностей редактора текстов ed. Если Вы — начинающий пользователь редактора текстов ed, то справоч- ная часть может оказаться весьма трудной для понимания. Не сдавайтесь! 3.3. ИСПОЛЬЗОВАНИЕ РЕДАКТОРА ТЕКСТОВ ed Редактор текстов ed — это интерактивная пре "рамма, предоставляющая пользователям возможность изменять содержимое файлов: перемещать отдельные фрагменты текста с места на место или удалять их из файла, заменять одни фрагменты текста другими. В содержимом файла редактор текстов ed различает символы, имеющие изображение, и несколько симво- лов, имеющих специальный смысл, таких как символ tab, и символ 1 * 3 1 Обучающая система learn— это интерактивная программа, предназначенная для обучения пользователей, далеких от программирования, применению ряда прог- раммных компонент ОС UNIX. Обучение ведется в процессе диалога с пользователем. Введя с терминала команду learn и отвечая на задаваемые вопросы, Вы сможете выбрать интересующую Вас тему и удобный режим обучения. Закончить урок можно, введя с терминала команду bye. Более подробно команда learn описана в learn (1 >. (Прим. ред.) 3 Зак lies 65
newline* 1. Редактор текстов ed имеет строчную ориентацию^, большинство его команд, естественно, применимы к одной или нескольким строкам как элементам файла строчной структуры, и, как правило, это полностью устраивает пользователя. Прежде чем заняться теорией, рассмотрим процесс использования редак- тора текстов ed. 3.4. ВЫЗОВ РЕДАКТОРА ТЕКСТОВ ed Увидев на терминале промптер интерпретатора команд shell, введите с терминала ed, и редактор текстов ed будет вызван. При желании можете добавить к команде аргумент — имя файла, который Вы хотите редакти- ровать. Указанный файл не обязательно должен существовать до вызова редактора текстов ed. Чтобы быть уверенным, что в текущем каталоге нет файла feed, введите с терминала следующую команду: rm fred а затем вслед за появлением на терминале промптера интерпретатора команд shell — команду ed fred Эти действия приведут к вызову редактора текстов ed, который сооб- щит Вам, присутствует ли в текущем каталоге файл fred. Если окажется, что файла fred в текущем каталоге нет, то редактор текстов ed выведет на терминал символ ?. Короче говоря, на терминале Вы увидете следующее: $ ed fred ? fred Появление на терминале символа ? — это реакция редактора текстов ed на все неожиданное. Конечно, это не слишком обильная информация, однако, узнав, что Ваша команда испортила редактору текстов ed настроение, Вы можете попытаться понять, что и почему его огорчило (в данном случае отсутство- вал файл fred). 1 Редактор текстов ed предоставляет пользователям возможность увидеть положе- ние символа, имеющего специальный смысл, в строке текста, изображая его с по- мощью стандартной для редактора текстов ed комбинации символов, обладающих изображением. Для этого следует ввести с терминала команду 1. Достаточно подроб- ное описание использования команды 1 приведено в приложении Б. {Прим, ред.] 1 Строкой текста обычно называют набор символов, заключенных между двумя последовательными символами newline или символами newline и EOT. Отдельного определения требует лишь первая строка текста (это набор символов, начинающийся первым символом некоторого файла и заканчивающийся первым встречным симво- лом newline). (Прим. ред.) 66
Теперь можно добавить какой-либо текст в файл fred. Действительно, файл fred не существовал до вызова редактора текстов ed, следовательно, он должен быть пустым, поэтому вполне логично было бы занести в этот файл некоторый текст. 3.5. КОМАНДА ДОБАВЛЕНИЯ СТРОК Чтобы сообщить редактору текстов ed о желании добавить какую-либо информацию в файл, введите с терминала команду а. Например, введя с терминала а They hit the bank of the Rio Grande At the height of the blazing noon; To slake their thirst and do their worst They sought Red Kate's saloon. Вы добавите в файл текст, заключенный между строкой, содержащей команду добавления текста а, и строкой, начинающейся с символа . и не содержащей более никаких символов. Так как редактор текстов ed не имеет своего промптера, то приведенный выше пример использования команды а будет в точности и полностью соответствовать тому, что Вы увидите на терминале, если попробуете воспроизвести этот пример. Символ . в отдельной строке, не содержащей более никаких символов, означает, что предыдущая строка является последней строкой добавляемого текста. Теперь запишите текст в файл fred и покиньте редактор текстов ed с по- мощью следующей последовательности команд: w Ч После ввода команды w (команды записи в выходной файл) редактор текстов ed сообщит Вам, сколько символов было записано им в выходной файл. В итоге Вы увидите на терминале следующее: w 143 q $ где символ $— это промптер интерпретатора команд shell, показываю- щий, что редактор текстов ed закончил свою работу. Убедиться в том, что файл fred содержит введенный в него текст, можно с помощью команды cat, позволяющей вывести содержимое файла на терминал: cat fred 3; 67
Итак, файл fred существует, находится в текущем каталоге и, наконец- то, содержит некоторый текст. Вызовите редактор текстов ed вновь. На терминале Вы увидите следующее: Sed fred 143 На этот раз редактор текстов ed не задает лишних вопросов — ведь файл fred существует. Редактор текстов ed осуществляет копирование содержимого файла fred в место, известное ему одному1, сообщая Вам, сколько символов им скопировано. Это число, конечно же, совпадает с числом, полученным Вами ранее в ответ на команду w. Очень важно отметить, что всякое редактирование производится только над содержимым буфера, которое выводится в выходной файл (в нашем случае файл fred) только по команде w. Если, запутавшись при редактировании некоторого файла, Вы захотите прервать процесс редактирования, чтобы начать все сначала, введите с тер- минала команду q. После такого выхода из редактора текстов ed содержи- мое редактируемого файла никак не изменится. Иногда команда q — это единственный путь к спасению. Редактируя файл (в нашем случае файл fred), Вы можете в любой мо- мент ввести с терминала команду w, чтобы вывести в выходной файл (в нашем случае файл fred) содержимое буфера редактора текстов ed. Если процесс редактирования затягивается, то, проделывая эту операцию систе- матически, Вы застрахуете себя от сбоев ЭВМ (вряд ли Вы будете доволь- ны, лишившись результатов пятичасового редактирования только потому, что инженер опрокинул в процессор чашку чая). Добавив к команде w аргу- мент anyfile. Вы сможете вывести содержимое буфера редактора текстов ed в файл anyfile. Если файл с таким именем уже существует, то его старое содержимое будет потеряно, в противном случае он будет создан. Ввод команды q означает немедленный выход из редактора текстов ed. Если Вы модифицировали содержимое буфера и, не пожелав вывести его в выходной файл, попробуете выйти из редактора текстов ed, послед- ний станет спорить с Вами: q are you sure? (у)1 2 1 Редактор текстов ed копирует содержимое входного файла ео временный файл, который согласно терминологии, принятой в Руководстве, называется буферам. Во избежание неточности, в дальнейшем изложении мы также будем пользоваться тер- мином "буфер". [Прим, ред.} 2 Вы уверены7 (да). [Прим. пер.) 68
Чтобы настоять на своем, введите с терминала у, этим Вы заставите редак- тор текстов ed выполнить Ваше желание. Аналогичного эффекта можно добиться, нажав клавишу, соответствующую специальному символу EOT. В обоих случаях редактор текстов ed безмолвно умрет и не станет больше с Вами препираться. Пользователи, которые работали до сих пор с редакторами текстов, автоматически выводящими результаты редактирования по окончании процесса редактирования, будут, возможно, несколько шокированы мето- дами, используемыми редактором текстов ed. Однако первый же случай "запутывания” при редактировании важного файла уничтожит создавшееся предубеждение. Но вернемся к примеру. Итак, редактор текстов ed скопировал содер- жимое файла fred в буфер и готов к редактированию. Давайте посмотрим, что он скопировал. Для этого воспользуйтесь командой р, которая выведет на терминал текущую строку тексте, или просто текущую строку. В данном случае текущей строкой будет строка They sought Red Kate's saloon На терминале Ваш диалог с редактором текстов ed будет выглядеть следующим образом: $ ed fred 143 Р They sought Red Kate's saloon. Обратите особое внимание на то, что после копирования редактором текстов ed содержимого файла fred в буфер, текущей строкой стала по- следняя строка текста. Команда а в этом случае будет добавлять текст после этой строки. Команду р можно применять не только для вывода на терминал текущей строки текста. Попробуйте ввести с терминала команды 2р и 1р и посмот- рите что получится; в результате на терминал будут выведены соответ- ственно вторая и первая строки текста. С таким же успехом команду р можно использовать для вывода на терминал строк текста с номерами из некоторого интервала. Например, по команде 1,4р на терминал будут выведены строки с первой по четвертую включительно (в нашем случае это весь текст, содержащийся в буфере). На самом деле аргументами целого ряда команд редактора текстов ed могут быть номера двух строк текста. Ввиду того что редактор текстов ed рассматривает файлы как объекты строчной структуры, будем впредь 'говорить об адресах строк в файле (точнее, в буфере). Воспользуемся введенными понятиями и сформулируем следующее утверждение: один 69
или два адреса, помещенные перед именем команды редактора текстов ed, указывают последнему, какая строка или какие строки текста должны быть обработаны данной командой. Проиллюстрируем сказанное на примерах, которые познакомят Вас с двумя новыми командами редактора текстов ed: командой г и командой d. С помощью команды г можно переписать (иначе говоря, скопировать) содержимое файла в буфер. Команда d служит для удаления строк текста из буфера. Необходимо отметить, что использование команды d требует чрезвычайной осторожности, ибо восстановить удаленную с помощью этой команды строку практически невозможно. Обратимся, наконец, к при- мерам: 1Р 1,4р 5r fred 1,6w temp 2d 1,2d За Если Вам не удалось разобраться в приведенной последовательности команд редактора текстов ed обратитесь к объяснениям: 1 р — выводит на терминал строку с адресом 1 1,4 р —выводит на терминал строки с адресами из диапазона 1-4 5r fred — копирует файл fred в буфер, помещая содержащийся в нем текст, начиная с адреса 6 1,6 w temp — записывает строки текста с первой по шестую из буфе- ра в файл 2d — удаляет из буфера строку с адресом 2 1,2 d — удаляет из буфера строки с адресами из диапазона 1—2 За — переводит редактор текстов ed в режим добавления текста, причем первой из строк добавляемого текста будет присвоен адрес 4. 3.6. НЕКОТОРЫЕ СПЕЦИАЛЬНЫЕ АДРЕСА До сих пор для указания адреса строки в буфере мы пользовались поряд- ковым номером этой строки, что в ряде случаев неудобно. Довольно часто, например, возникает необходимость указать адрес последней строки текста, порядковый номер которой, вообще говоря, неизвестен. Для реше- ния этой проблемы воспользуемся предоставляемой редактором текстов ed возможностью символического обозначения адресов строк текста. Так, для редактора текстов ed символ $ является стандартным обозначе- нием адреса последней строки в буфере. 70
Тогда, для того чтобы вывести на терминал все содержимое буфера достаточно ввести с терминала команду 1,$р Если Вам надоело ждать завершения вывода (ожидание завершения вывода большого файла — весьма утомительно), нажмите на терминале клавишу, соответствующую специальному символу INTERRUPT, и прервите его. Понятно, что нет необходимости в аналогичном символе для обозначения адреса первой строки буфера редактора текстов ed, так как это всегда 1. Большую роль при редактировании файлов играет адрес текущей строки буфера. Если команды, такие как р, a, d и так далее введены без адресов, то редактор текстов ed по умолчанию будет обрабатывать лишь одну стро- ку — текущую строку буфера. В качестве стандартного для редактора текстов ed обозначения адреса текущей строки текста в буфере принят символ.. Проиллюстрируем использование стандартного обозначения адреса текущей строки в буфере на следующем примере: . - 6,. + 6р .-1 . + 1 В результате выполнения первой из этих команд на терминал будут выведены строки с адресами из диапазона .—6 —.+6, т. е. шесть строк, пред- шествующих текущей строке текста, затем текущая строка текста и, нако- нец, шесть строк, следующих за текущей строкой текста. В результате выполнения второй из этих команд на терминал будет выведена строка, предшествующая текущей строке текста, а в результате выполнения третьей команды — строка, следующая за текущей строкой текста. В двух послед- них командах отсутствует имя команды р. Редактор текстов ed по умолча- нию полагает, что, задав лишь адреса строк. Вы хотите вывести на терминал строки с указанными адресами. Применив указанные команды одну за дру- гой, Вы столкнетесь с неожиданным, видимо, для Вас фактом — некоторые команды редактора с текстов ed, в том числе и команда р, изменяют адрес текущей строки текста. После применения команды р адрес текущей строки текста становится равным адресу последней выведенной на тер- минал строки. Так, по команде 5 редактор текстов ed выведет на терминал строку текста с адресом 5 и установит адрес текущей строки текста равным 5. Адрес текущей строки текста можно узнать с помощью команды Для вывода предшествующей или последующей по отношению к теку- щей строке текста чаще всего используются две команды вида .+ 1 .-1
Однако есть и более короткий путь — нажмите на терминале клавишу return; это приведет к увеличению адреса текущей строки текста на 1, а затем к выводу на терминал текущей строки буфера. Теперь введите с терминала символ —, на этот раз адрес текущей строки текста умень- шится на 1, после чего текущая строка буфера будет выведена на терминал. В заключение отметим, что стандартная версия редактора текстов ed не предоставляет пользователю дополнительных удобных средств для вывода на терминал окрестности текущей строки текста, находящегося в буфере (в некоторых редакторах текстов для обозначения окрестности теку- щей, строки вводится термин "окно"). И это очень обидно, так как, во-пер- вых, такая операция крайне необходима всем пользователям редактора текстов ed, а во-вторых, существует уже достаточно "самодельных" версий редактора текстов ed, в которых такие средства имеются. И все-таки, заботясь в первую очередь о мобильности редактора текстов ed, мы остаемся при своем мнении и отвергаем всякие его усовершен- ствования. 3.7. ПРЕДВАРИТЕЛЬНОЕ РЕЗЮМЕ Итак, теперь Вы узнали, как можно редактировать файл с помощью команд a, w, q, р, г и d, познакомились с принципами адресации строк в буфере, но, прежде чем закончить изучение этого подмножества команд редактора текстов ed, дополним его еще одной командой; речь идет о команде i. Команда i вызывает со стороны редактора текстов ed действия, аналогичные его действиям по команде а, с той лишь разницей, что по команде а текст добавляется в буфер после строки текста-с указанным в команде адресом, а по команде i текст вставляется перед ней. Напри- мер, последовательность команд 5i hello приводит к тому же результату, что и 4а hello Мы настоятельно рекомендуем воспроизвести этот и предыдущие при- меры — это поможет Вам быстрее освоить команды редактора текстов ed из уже описанного подмножества. Во время своих экспериментов не за- бывайте следить за адресом текущей строки текста и проверять результат применения той или иной команды, выводя на терминал содержимое буфера с помощью команды 1,$р 72
Не забывайте, что редактор текстов ed не изменит содержимого редак- тируемого Вами файла до тех пор, пока Вы не введете с терминала коман- ду w. И, наконец, последний совет — большинство команд редактора текстов ed допускает присоединение к ним команды р для мгновенного вывода на терминал результата выполнения команды. Например, по команде dp редактор текстов ed удаляет текущую строку текста из буфера и выводит на терминал строку, ставшую текущей после выполнения этой операции. На практике обычно применяют сочетание команды р и команды контекст- ной замены. Такой прием позволяет следить за происходящей в буфере модификацией текста. 3.8. КОМАНДА КОНТЕКСТНОЙ ЗАМЕНЫ Команда s является одной из наиболее важных команд редактора текстов ed, поскольку используется для изменения текста в буфере с по- мощью замены одного фрагмента текста другим. Будем называть такие фрагменты текста контекстами. Для иллюстрации действия команды контекстной замены воспользуемся уже знакомым Вам четверостишием — мы использовали его при описании команды а. Если Вы следовали нашим рекомендациям и воспроизводили предлагаемые примеры, если Вам, кроме того, сопутствовала удача, то теперь Вы являетесь владельцем файла, содержащего указанное четверостишие. В этом случае повторите процедуру вызова редактора текстов ed, копирования Вашего текстового файла в буфер и установите адрес текущей строки текста равным 1. Тогда текущая строка будет выглядеть следующим образом: They hit the bank of the Rio Grande Введите с терминала команду s/bank/banks/ Редактор текстов ed не выводит на терминал результатов применения команд автоматически, а потому, введя указанную команду, Вы не уви- дите результатов на терминале. Чтобы увидеть, что все-таки произошло с текущей строкой буфера, воспользуйтесь командой р. Если Вы не сде- лали ошибки, то на терминал будет выведено They hit the banks of the Rio Grande т. e. вместо контекста bank в строке появился контекст banks. В обобщенной форме команда s выглядит следующим образом: адрес1, адрес2в/заменяемый контекст/заменяющий контекст/ В результате выполнения этой команды во всех строках буфера с адресами из диапазона "адрес!" — "адрес2" вместо "заменяемого текста" будет подстав- лен "заменяющий контекст", при этом символ / не должен входить ни 73
в "заменяемый контекст", ни в "заменяющий контекст". Адрес текущей строки текста установится равным адресу последней измененной строки текста. ВНИМАНИЕ! Для всех строк буфера с адресами из интервала "адрес!" — "адрес?" контекстная замена "заменяемого контекста" на "заменяющий контекст" будет осуществлена только для первого вхождения "заменяемо* го контекста" в каждую из этих строк. Чтобы проделать операцию контекстной замены над всеми имеющимися в строках с адресами из указанного интервала вхождениями "заменяемого контекста" в каждую из этих строк, необходимо присоединить к имени команды контекстной замены символ g так, как это показано ниже: 1,4s/th/xxx/g В этом случае все вхождения контекста th во всех четырех строках буфера будут заменены на контекст ххх. Попробуйте проделать эту операцию и посмотрите, что получится, а затем восстановите исходный текст. Если в команде s задан лишь один адрес, то и обработана будет лишь одна строка с этим адресом. Аргумента "заменяющий контекст" может и не быть вовсе; такая контекстная замена эквивалентна удалению "заменяемого контекста" из группы строк текста, к которым применяется команда s. Например, команда s/xyz// заменяет в текущей строке первое вхождение контекста xyz на пустой контекст, т. е. на контекст, не содержащий ни одйого символа. Очевидно, что такая контекстная замена равносильна удалению контекста xyz из текущей строки. До сих пор во всех примерах контекстной замены символ / рассматри- вался как символ, резервируемый для нужд редактора текстов ed. На са- мом деле роль символа / может исполнять любой символ, не входящий в состав контекстов — аргументов команды s и не являющийся символом и или символом tab. Выполнение двух следующих команд: s/xx/yy/ saxxayya приводит к одинаковым результатам. В результате выполнения каждой из этих двух команд редактор текстов ed производит в текущей строке бу- фера замену первого вхождения контекста хх на контекст уу. Таким об- разом, ограничение на вид служебного символа в формате команды кон- текстной замены снято. И все-таки не торопитесь — целый ряд символов, таких как символы Л, ., $, [, », &, редактор текстов ed использует исключительно в своих целях — и во избежание неприятностей не пользуй- тесь ими пока в своих экспериментах. 74
3.9. КОНТЕКСТНЫЙ ПОИСК Если Вы никогда не пользовались редактором текстов, подобным редак- тору текстов ed,TO Вам будет трудно определить степень важности для пользователя тех или иных его команд. Тем не менее только совершенно неопытный пользователь может опустить процедуру контекстного поиска в списке необходимейших возможностей, предоставляемых редактором текстов пользователям. Что же такое контекстный поиск? Мы уже говорили, что редактор текстов ed обрабатывает содержимое файлов как набор строк, каждой из которых присвоен адрес. Удобно ли это при редактировании Ваших файлов, определяется, по-видимому, их струк- турой. Проблема заключается в том, что всякое применение команд and при редактировании файлов изменяет, вообще говоря, адреса некоторых из его строк. В такой ситуации целесообразно ссылаться на конкретные строки текста не по их адресам, а по их содержимому. Так или иначе, но следующий пример иллюстрирует интересную возможность редактора текстов ed. В ре- зультате выполнения команды /аЬс/ редактор текстов ed устанавливает адрес текущей строки текста равным адресу строки, содержащей контекст аЬс, при этом поиск такой строки осуществляется от строки, следующей за текущей строкой буфера до конца последнего, и в случае неудачи — продолжается от начала буфера до текущей строки текста. Назовем такое направление поиска прямым. При желании можно вести поиск в обратном направлении, для этого необходимо ввести с терминала команду ?abc? Подобные богатые возможности редактора текстов ed в области контекст- ного поиска наводят на мысль об использовании их для адресации строк бу- фера при применении команд редактора текстов ed. Проиллюстрируем это на примере. Введите с терминала команду /а/ /b/s/123/456/ В этом примере редактор текстов ed производит замену контекста 123 на контекст 456 в первой найденной в результате поиска в прямом направлении строке буфера, содержащей контекст 123. Поиск строки, содержащей кон- текст 123, осуществляется вгруппе строкбуфера,определяемой следующим образом: начиная со строки,следующей за текущей строкой буфера,редактор текс- тов ed осуществляет поиск в прямом направлении строки, содержащей контекст а; начиная с этой строки, редактор текстов ed осуществляет поиск строки, содержащей контекст Ь, включая названную выше группу строк все анали- зируемые при этом строки буфера, в том числе и саму строку, содержащую контекстЬ.
Очень часто перед Вами будет стоять задача отыскания в файле строки, содержащей некоторый контекст. С помощью редактора текстов ed.Bbi без труда справитесь с этой задачей. Однако возможно, что ни первая, ни вторая, ни даже пятая из найденных и содержащих заданный контекст строк буфера не удовлетворит Вас и Вы будете раз за разом вводить с терминала команду контекстного поиска, дабы продолжить его. Эта процедура ничуть не раз- нообразнее многочасового печатания на пишущей машинке какой-нибудь тарабарщины, вроде названия Валлийского городка [5] Llanrhaedr-yng-Nghinmeirch К счастью, редактор текстов ed умеет запоминать последний контекст, заданный Вами в команде контекстного поиска. Назовем этот запомина- емый редактором текстов ed контекст шаблоном. Для поиска строки, со- держащей шаблон, достаточно ввести с терминала команду // Таким образом, последовательность команд /Deadeye Dick/ // // приводит к контекстному поиску в прямом направлении трех строк, содержащих контекст Deadeye Dick. Если в тексте, находящемся в буфере, имеется лишь одна такая строка, то в результате будет трижды найдена одна и та же строка. Запоминаемый редактором текстов ed шаблон может быть использован в команде s, что иллюстрируется следующим примером: /Deadeye Dick/s//Mexico Pete/ //s/// Применение этой последовательности команд приводит к следующим резуль- татам: редактор текстов ed делает попытку найти строку, содержащую кон- текст Deadeye Dick; если это удается, то контекст Deadeye Dick заменяется на контекст Mexico Pete. Далее вновь ищется строка, содержащая контекст Deadeye Dick, и, если такая строка найдена, то она удаляется из буфера. Прежде чем перейти к следующему параграфу, убедитесь в том, что Вам все понятно в этом! 3.10. КОМАНДА ЗАМЕНЫ СТРОК Команда с используется для замены одной или более строк на какую- либо другую последовательность строк. Чтобы воспользоваться командой с, необходимо указать номера заменяемых строк текста, а затем ввести новые строки так, как если бы Вы пользовались командой а или i. Приводимые ниже примеры демонстрируют замену седьмой строки на строки ааа, bbb, ссс: 76
7с ааа bbb ссс Для замены нескольких строк задайте номера двух строк, например 3,7с. Тогда в приведенном выше примере редактор текстов ed заменит строки с третьей по седьмую на строки ааа bbb ссс Если адрес строки не указан, то редактор текстов ed, как обычно, будет опе- рировать текущей строкой текста. Команда с не является, строго говоря, необходимой, так как ее действие эквивалентно действию комбинации таких команд, как d и i или d и а. Однако в ряде случаев пользоваться командой с намного удобнее, чем ука- занной комбинацией команд. 3.11. КОМАНДА ПЕРЕМЕЩЕНИЯ СТРОК Команда m используется для перемещения одной или более строк из одной части буфера в другую его часть. В обобщенной форме команда m выглядит следующим образом: первый адрес, последний ад реет адрес вставки При этом строки с адресами из диапазона "первый адрес" — "последний адрес" последовательно перемещаются в часть буфера непосредственно за строкой с адресом "адрес вставки”. Например,чтобы извлечь из текста стро- ки с адресами 7, 8, 9, 10, 11 и 12 и присоединить их к его концу, необходи- мо ввести с терминала команду 7,12ш$ Если Вы укажете перед именем команды перемещения текста лишь один адрес, то перемещена будет лишь одна строка с указанным адресом. И, как обычно, вместо любого отсутствующего адреса строки редактор текстов ed по умолчанию вставит адрес текущей строки текста. Для вставки текущей строки между пятой и шестой строками текста применяется команда m 5 Команда m всегда перемещает строки с адресами, указанными перед именем команды, за строку с адресом, указанным после имени команды. В резуль- тате выполнения команды m текущей строкой станет последняя из переме- щенных строк. Попробуйте сами! 77
3.12. СИМВОЛЫ, ИМЕЮЩИЕ ДЛЯ РЕДАКТОРА ТЕКСТОВ ed СПЕЦИАЛЬНЫЙ СМЫСЛ Вспомним те символы, которые имеют для редактора текстов еЦспециаль- ный смысл и о которых мы предупреждали Вас ранее. В этом параграфе мы попытаемся объяснить их значение. Рассмотрим вновь символы А. $[ * \ & Первые пять из них используются при проведении контекстного поиска для задания шаблона. Их использование практически ничем не отличается от ис- пользования метасимволов интерпретатора команд shell. Наверное, Вы не поняли этого сразу, как, впрочем, и мы. Особого внимания заслуживает символ \ . Каждый из приведенных семи символов, имеющих для редактора текстов ed специальный смысл, утратит этот специальный смысл, если перед ним поставить символ \, т. е. символ \ обеспечивает экранирование специального смысла этих символов. Обычный символ с поставленным перед ним символом \ эквивалентен самому себе. Проиллюстрируем это на примере команды s/\*\\\$/abc/ в результате выполнения которой в текущей строке контекст будет заменен на контекст аЬс. В приведенной команде s каждому символу, имеющему для редактора текстов ed специальный смысл, в том числе и самому символу \, предшест- вует символ \. Однако если в удаляемом из строки контексте нет других символов, имеющих для редактора текстов ed специальный смысл, кроме символа \, то предварять его самого символом \ нет необходимости. Если Вы не поняли почему это так, то попробуйте проделать указанную контекстную замену и убедитесь в нашей правоте. Рассмотрим теперь подробнее символы, имеющие для редактора текстов ed специальный смысл. 3.12.1 . символ Л В шаблоне символ Л равносилен несуществующему (нулевому) символу строки текста, т. е. символу, стоящему перед первым символом строки. Вы можете пользоваться символом Л в тех случаях, когда необходимо, чтобы шаблон совпадал с первыми символами просматриваемых строк буфера. В следующих примерах в результате выполнения первой из приведенных команд осуществляется поиск в прямом направлении строки буфера, содер- жащей контекст first в любом месте строки, а в результате выполнения второй из приведенных команд осуществляется поиск в прямом направле- нии строки буфера, начинающейся с контекста second: /first/ /Л second/ 78
3.12.2 . СИМВОЛ $ Символ $ выполняет в редакторе текстов ебдве функции: символичес- кого адреса последней строки и в шаблонах для обозначения символа, стоя- щего после последнего символа строки1. По своему положению в строке символ, обозначаемый с помощью символа $, противостоит символу, обоз- начаемому с помощью символа Л, что позволяет найти конец строки: s/$/end/ В результате выполнения приведенной выше команды контекст end поме- щается сразу за последним символом в текущей строке текста. С помощью символов $ и Л Вы сможете найти строку, в точности совпадающую с задан- ным шаблоном. Команда /Ajust thisJ/ означает контекстный поиск строки, состоящей только из контекста just this. 3.12.3 . СИМВОЛ . У точки работа еще тяже- лее, чем у доллара. Когда символ . используется в качестве адреса строки, то он означает ад- рес текущей строки. В шаблоне символ . эквивалентен произвольному сим- волу. Например, в результате выполнения команды /а.Ь/ могут быть найдены, например, такие строки: aab abb а.Ь асЬ и т. д. С помощью команды /Л. .4/ Вы найдете строку, содержащую только два символа (неважно какие). 1 Символу $ на отечественных терминалах соответствует символ Й. [Прим, ред.) 79
3.12.4 . СИМВОЛ * Звездочка — это очень жирная точка. Символу ♦ обязательно должен предшествовать какой-либо другой сим- вол. В совокупности они эквивалентны контексту, состоящему из произ- вольного числа символов, предшествующих символу * (включая пустой кон- текст, не содержащий ни одного символа). Например, команды контекстного поиска /х*/ /.*/ производят контекстный поиск строки, содержащей соответственно кон- текст из произвольного числа символов х и контекст из произвольного числа произвольных символов. Понятие произвольного числа, включающее в себя и понятие "ни одного", что обычно приводит новичков в состояние оцепени- ния. По шаблону abc* может быть найдена любая из нижеследующих строк: ab abc abcc abccc abcccc В такой ситуации редактор текстов ed выберет наиболее длинную из найден- ных строк, т.е. строку abcccc. Сказанное означает, что в результате выполне- ния команды s/abc*/123/ контекст abcccc будет заменен на контекст 123. 3.12.5 . СИМВОЛ [ Символ [ используется для перечисления произвольных символов и всегда сопровождается символом ]. В следующих примерах: /[abc]/ /[def] / /[abc] ] 123]/ в качестве шаблона может быть выбран любой из следующих контекстов соответственно: а,Ь или с а,е или f a,b или с с последующими цифрами 1,2 или 3 Если между символами [ и ] стоят только цифры, представляющие собой последовательные элементы натурального ряда, то запись вида [1234] 80
эквивалентна записи [1-4] Если же символы, заключенные между символами [ и ], являются только буквами и при этом лексикографически упорядочены, то запись вида [abcdefg] в свою очередь эквивалентна записи [а-д] Если у Вас возникло желание включить в число символов , заключенных между символами [ и ], символ ], то поставьте его первым в группе симво- лов, заключаемых между символами [ и ]. Например, запись []123] задает следующую группу шаблонов: ], 1, 2 или 3 Если же первым символов в группе символов, заключенных между сим- волами [ и 1, Вы поставите символ Л, то в множество шаблонов будут вклю- чены все допустимые символы, за исключением тех, что заключены между символами [ и ] и следуют за символом Л. Например, запись [Л 1-4] определяет любой допустимый символ, кроме 1, 2, 3, 4, как шаблон. 3.12.6 . СИМВОЛ & Символ &, который является кратким символическим обозначением "заменяемого контекста", может располагаться только справа от контекста, заменяемого командой s на другой контекст. Например, в результате вы- полнения команды s/th is/& and that/ производится замена контекста this на контекст this and that. А выполнение последовательности команд /Л hello $/s//& & &/ а what's all this then? приводит к тому, что редактор текстов ed находит строку, содержащую лишь контекст hello заменяет этот контекст на контекст hello hello hello, а затем присоединяет к текущей строке текста строку what's all this then? Забывая об этом специальном смысле символа & для редактора текстов ed, многие программисты, использующие для решения своих задач язык 81
программирования Си, часто впадают в ошибку при попытке заменить в тексте своей программы символ логического ИЛ И на символ логического И. Обычно это происходит так: введя с терминала команду S/II/&&/ они получают неожиданный результат 1111, В то время как необходимые им действия выполняются с помощью команды 5/II /\&\&/ (они забыли, что специальный смысл символа & может быть экранирован с помощью символа \). 3.13. КОМАНДЫ УКАЗАНИЯ ГЛОБАЛЬНОСТИ Завершая описание редактора текстов ed до конца книги, рассмотрим еще две команды. Команда g позволяет распространить действие некоторой другой коман- ды на все находящиеся в буфере строки, содержащие некоторый шаблон. Например, выполнение приведенной ниже команды приводит к выводу на терминал всех имеющихся в буфере строк, содержащих контекст Eskimo: g/Eskimo/p В ходе выполнения приведенной выше команды редактор текстов ed прос- матривает весь буфер, находит все имеющиеся в буфере строки, содержа- щие контекст Eskimo и запоминает их адреса. Затем редактор текстов ed последовательно устанавливает в качестве текущей строки текста значения запомненных адресов, (следуя в обратном порядке, и иыполняет указанную в совокупности с командой g команду, в данном случае команду р. Эта процедура нив коем случае не изменит адресов, хранящихся в буфере строк, так как редактор текстов ed оперирует найденными строками текста,уста- навливая их поочередно текущими. В обобщенной форме команда g выгля- дит следующим образом: д/шаблон/любые команды На месте "любых команд" может стоять произвольная последовательность любых команд редактора текстов ed. Рассмотрим следующий пример: g/abc/s/def/ghi/ В ходе выполнения приведенной последовательности команд редактор текстов ed просматривает весь буфер и запоминает адреса строк, содержа- щих шаблон аЬс. По окончании поиска в каждой из найденных строк, если такие строки существуют, первое вхождение во все найденные строки кон- текста def заменяется на контекст ghi. Добавление к этой последовательнос- ти команд команды g приведет к тому, что контекстная замена будет про- ведена для всех вхождений контекста def во все найденные строки: g/abc/s/def/gh i/g 82
И, наконец, несколько слов о команде v, названной так неизвестно поче- му. Действия, производимые командой V, совершенно аналогичны действи- ям команды g, с той разницей, что при использовании команды g редактор текстов edоперирует строками, содержащими некоторый шаблон, а при использовании команды v— строками, не содержащими некоторый шаблон. В следующем примере редактор текстов ed выводит на терминал все строки,не содержащие контекста, (символ . имеет для редактора текс- тов ed специальный смысл, поэтому в шаблоне перед ним стоит символ /): v/\./p Допустим, что у Вас возникло желание ввести с Вашего терминала последо- вательность команд редактора текстов ed так, чтобы каждая команда из этой последовательнооги была бы расположена на отдельной строке, или же заменить каждую строку текста, содержащую контекст bold, на последова- тельность строк not so bold В этом случае воспользуйтесь символом \. Например: g/bold/\ с\ not\ so\ bo IdX Каждая строка (кроме последней) из приведенной выше последовательнос- ти строк завершается символом \.,Этс означает, что она будет продолжена на следующей строке. Указав интервал строк, Вы можете ограничить область контекстного по- иска. Так, по команде 1,4д/а/р редактор текстов ed производит контекстный поиск лишь среди строк с адресами из. интервала 1 — 4. Подумайте, какие действия выполняет редак- тор текстов ed затем. 3.14. ЗАКЛЮЧЕНИЕ Итак, это все. Если Вы изучили и поняли весь предложенный материал, то, по-видимому, теперь вполне способны бороться с редактором текстов ed. Однако не возноситесь — впереди еще много работы. В приложении Б содержится краткая сводка команд редактора текстов ed, в том числе ко- манды, в данной главе не описанные. Прочитайте внимательно приложение Б. Один раз сейчас и второй раз после того, как поработаете с редактором 83
текстов ed. Возможно чтобы будете удивлены тем, как много нового узнаете. Но не тратьте времени на изучение хитроумных трюков, если Ваша задача может быть решена с помощью простых действий. Опасайтесь испортить сложный текстовой материал, не располагая достаточным временем для ре- дактирования. Усилия, затраченные на детальное изучение редактора текстов ed, оку- пятся лишь в том случае, если Вы будете заниматься редактированием текс- тов регулярно, например, если Вы собираетесь использовать команды редак- тора текстов ed в качестве операторов некоторого языка программирования, а сам редактор текстов ed —в качестве интерпретатора.Такая необходимость возникает, если Вам приходится преобразовывать исходные данные или же результаты работы Вашей программы к нужному формату. Для большинст- ва пользователей вполне достаточно простейших команд редактора текстов ed, вроде a, i, w, s, d p и простейшего контекстного поиска. Г Л А В А 4. ЯЗЫК ПРОГРАММИРОВАНИЯ Си 4.1 ВВЕДЕНИЕ Написать эту главу было нелегко. С одной стороны, невозможно не упо- мянуть о языке программирования Си в книге, посвященной описанию ОС UNIX. С другой стороны, рассказать о языке программирования Си в одной главе просто невозможно. Поэтому, по-видимому, эта глава никому не по- нравится. Зная теперь обо всех трудностях, связанных с написанием этой главы, надеемся, что Вы не будете судить нас слишком строго. В этой главе мы попытались изложить jot минимум сведений о языке программирования Си, который, по нашему мнению, наиболее полезен для тех, кто будет лишь изредка применять язык программирования Си. Такие пользователи могут рассматривать эту главу как справочное руководство. Конечно,эта глава не претендует на полноту описания языка программиро- вания Си. Порядок изложения материала в этой главе не случаен,.а выбран нами намеренно, после долгих размышлений. Он существенно отличается от обще- принятого порядка изложения материала по этому вопросу, чтобы тому, кто решит прочитать всю эту главу целиком за один раз, не стало скучно. 4.2. ПРЕДВАРИТЕЛЬНЫЕ ЗАМЕЧАНИЯ Для большей части программистов, использующих ОС UNIX, язык програм- мирования Си и ОС UNIX неразделимы. Говоря о языке программирования Си, они неявно предполагают, что речь идет об ОС UNIX, и,наоборот, говоря об ОС UNIX, они имеют в виду и язык программирования Си. Операционная система UNIX написана почти полностьюнаязыке программирования Си.Уси- лия, затраченные на то, чтобы ОС UNIX стала мобильной операционной систе- мой, привели к тому, что язык программирования Си превратился в один из 84
наиболее мобильных языков программирования*, особенно если в качестве целевой операционной системы предполагается использовать ОС UNIX или подобную ей операционную систему. Программа, Написанная на языке про- граммирования Си и отлаженная в ОС UNIX на одной ЭВМ, должна без каких-либо изменений правильно выполняться в ОС UNIX на другой ЭВМ, если только Вы не постараетесь специально написать немобильную програм- му. Однако даже если Вы напишете немобильную программу, то ОС UNIX предоставляет Вам средство, которое облегчает превращение такой прог- раммы в мобильную. Для этого в ОС UNIX имеется программа lint (см. lint (1)), которая обеспечивает выявление всех мест в программе, которые могут вызвать затруднения при переносе этой программы на другую ЭВМ. Нам бы хотелось, чтобы у Вас создалось впечатление, будто мы счита- ем, что язык программирования Си — это лучший в мире язык програм- мирования из когда-либо существовавших. Споры о том, какой из языков программирования является "самым лучшим", будут продолжаться до тех пор, пока на земле не исчезнут все глупцы, однако у нас нет ни малейшего желания ввязываться в подобные споры. Спорить на эту тему имеет не больше смысла, чем спорить о том, какой из семи цветов радуги является наиболее красивым. Итак, не ввязываясь ни в какие споры, мы хотели бы только отметить, что для реализации ОС UNIX, которая, кстати, была осу- ществлена двумя очень способными людьми, был выбран именно язык про- граммирования Си, а не какой-либо другой язык программирования, хотя у этих людей было из чего выбирать1 2. 4.3. ПЕРВЫЕ ШАГИ Из-за недостатка места мы сразу перейдем к делу. Используя редактор текстов ed, создайте файл hi.с и поместите в этот файл следующий текст: main () {printf ("hello sailor\n");} Теперь осуществите трансляцию этого текста с помощью команды сс hi.с Если Вы не допустили ошибок, то на терминал будет выведен только промп- тер интерпретатора команд shell. Для того чтобы выполнить транслирован- ную программу3 4, которая всегда помещается в файл a.out*, введите с тер- минала a.out 1 По-видимому, это сказано слишком сильно. (Прим, ред,} 1 Более того, язык программирования Си был специально создан одним из этих людей именно для этого. (Прим. ред.) 3 На самом деле в результате выполнения команды сс без флагов Ваша программа будет не только оттранслирована, но и скомпонована. (Прим, ред.) 4 На самом деле, используя флаг —о. Вы можете поместить результаты работы команды сс в файл с произвольным именем. См. приложение А. (Прим, ред.) 85
Если Вы аккуратно и без ошибок проделали все эти действия, то на терми- нале Вы увидите следующее: $сс hi.с Sa.out hello sai lor $ Если Вы хотите сохранить оттранслированную программу, то Вы должны переименовать файл a.out до того, как начнете транслировать следующую программу. Для этого можно воспользоваться командой mv. Только что Вы осуществили трансляцию программы, состоявшей из од- ной-единственной функции, которая, в свою очередь,"содержала единст- венный выполняемый оператор. Все программы на языке программирования Си состоят, по крайней мере, из одной функции. Среди функций, из которых состоит программа на языке программирования Си, обязательно должна быть функция main, с которой всегда начинается выполнение программы. В приведенном выше примере программа на языке программирования Си состоит из функции main, которая вызывает функцию printf с аргументом "hello sailor\n". В отличие от языка программирования Фортран, в котором имеются опе- раторы ввода-вывода (например, оператор write), в языке программирова- ния Си операторы ввода-вывода отсутствуют. Функция printf, широко ис- пользуемая в программах на языке программирования Си, является просто программой, которую кто-то написал и поместил в библиотеку полезных функций. Упоминание этой функции в Вашей программе привело к тому, что эта функция была найдена в библиотеке и включена в Вашу программу. В языке программирования Си отсутствуют встроенные функции. Вместо них используются библиотечные функции. Функция printf является одной из таких библиотечных функций. 4.4. СТРОКИ Аргументом функции printf в приведенном выше примере являлась стро- ка. Строка — это последовательность символов, заключенная между симво- лами "и". В языке программирования Си строка трактуется как последова- тельность символов, завершающаяся символом null (нуль), обеспечиваю- щим возможность идентифицировать конец этой строки. Для отображения символов, не имеющих своего представления на печати, введена специальная нотация с использованием символа Ниже приведен перечень всех таких символов; \t - tab \r — carriage return \n — newline \b — backspace \\ -\ 86
— quote " \f — form feed Программы на языке программирования Си представляют собой со- вокупность функций. Порядок следования функций при этом несуществен, в отличие от ряда других языков программирования. Обязательным яв- ляется только наличие функции main. Ниже приведен еще один пример программы на языке программирования Си, которая обеспечивает выпол- нение тех же действий, что и ранее, с тем лишь отличием, что в этом приме- ре функция main, для того чтобы выполнить "черную работу", дважды обращается к функции speak: main(){ speak(); speak (); } speak(){ printff'heilo sailor\n"); Порядок следования функций в приведенном выше примере может быть изменен. Попробуйте сделать это и посмотрите, что получится. 4.5. ТИПЫ ДАННЫХ Естественно, что для выполнения каких-либо полезных действий Вы должны научиться манипулировать данными, т. е. числами или символами. Язык программирования Си имеет вполне достаточный набор типов данных и средств структурирования данных. На первое время мы ограничимся толь- ко следующими типами данных: char, int и double, которые соответствуют символам, целым числам и числам удвоенной точности в формате с плаваю- щей точкой. 4.5. ПЕРЕМЕННЫЕ И КОММЕНТАРИИ В языке программирования Си любая переменная должна быть описана1 до того, как она будет использована. Если переменная описана вне функ- ций, составляющих программу, то такая переменная является глобальной, т. е. доступна в любой функции, входящей в состав программы. Если пе- ременная описана внутри некоторой функции, то такая переменная являет- ся локальной, т. е. доступна только в той функции, в которой она описана. Это означает, что конфликта имен для локальных переменных с одними и теми же именами, но описанных в разных функциях не возникает. В языке программирования Си разрешается использовать комментарии. 1 Описание переменной в языке программирования Си связывает имя переменной с типом данных, классом памяти, областью действия и значением. [Прим, ред.} 87
Комментарием в языке программирования Си считается все, что заключено между символами /« и */. Комментарии могут быть помещены в любом месте программы, где могут стоять символы <_j или newline. Ниже при- веден пример программы на языке программирования Си с комментария- ми, которая выводит целые числа от 1 до 10 и их квадраты: тагп(){ /* программа печати целых чисел от 1 до 10 и их квадратов */ int i,isquared; /* описание переменных */ for(i = 1; i < = 10; i = i+ 1){ /* цикл */ isquared = i*i; printf("%d %d\n",i,isquared); }/* конец цикла »/ } Приведенный выше пример требует пояснений. В теле функции main самая первая строка является комментарием. В следующей строке описаны две целые переменные, используемые этой функцией: i и isquared. Несколько переменных одного типа могут быть описаны в одной строке, при этом имена переменных должны отделяться друг от друга с помощью символа ,. Имена переменных должны начинаться либо с буквы, либо с символа _ и могут содержать строчные и прописные буквы латинского алфавита, цифры от Одо 9 и символ _ . Максимальная длина имен переменных зависит от конкретного типа ЭВМ. Вообще гово- ря, в языке программирования Си допускается использование имен пере- менных произвольной длины, однако их различение производится в раз- личных трансляторах по нескольким первым символам (не меньше шести). Следующая часть программы — это цикл, управляемый оператором цикла for. Вначале переменной I присваивается значение, равное 1. Затем до тех пор, пока значение переменной i меньше или равно 10, выполняется тело цикла. Каждый раз после выполнения цикла значение переменной i увеличивается на 1. Особое внимание Вы должн1»1 обратить на расположе- ние символов ; в заголовке оператора цикла for и внутри тела этого опе- ратора. Если бы Вы захотели выполнить некоторый цикл от 100 до 10 с шагом —5, то заголовок оператора цикла for в этом случае выглядел бы следую- щим образом: for (i = 100;i>= 10; i = i-5){ По-видимому, Вы удивлены обилием в программе символов {и]. Сим- волы {и} используются для того, чтобы сгруппировать вместе несколько операторов, т. е. эти символы выполняют функции, выполняемые опера- торами begin и end в других языках программирования. В приведенном выше примере первый символ {и последний символ} отмечают начало и конец тела функции main. Вторая пара символов {и } отмечает начало и 88
конец тела оператора цикла for. Если бы тело оператора цикла for состоя- ло только из одного оператора, то символы { и } можно было бы опустить. На самом деле в приведенном выше примере использование в теле цикла двух операторов не является необходимым, так как аргументы функции printf могут быть выражениями. В приведенном выше примере аргументами функции printf являлись: строка "%d%d\ п" переменная i переменная (squared Заменяя в аргументах функции printf переменную (squared на выражение i*i и опуская комментарии, программу из приведенного выше примера можно переписать следующим образом: main(){ int i; for(i = 1; i < = 10; i = i + 1) printf("%d %d\n",i,i*i); } Еще раз хотим напомнить Вам, что функция printf является не частью языка программирования Си, а библиотечной функцией. Функция printf будет использоваться нами настолько часто, что здесь необходимо дать хотя бы минимальные пояснения (более подробные пояснения по поводу ис- пользования библиотечных функций Вы сможете найти в гл. 9). Функция printf применяется для форматирования и вывода на стандартный вывод результатов работы программы, сообщений и т. д. Первым аргументом функции printf является строка (на самом деле, указатель1 на строку), которая должна быть выведена на стандартный вывод. Символ % является для функции printf признаком того, что форматированию и выводу подлежит один из ее последующих аргументов. В приведенном выше при- мере символы %d означают, что вывод чисел необходимо осуществить в десятичном виде. При использовании символов %f вывод чисел осу- ществляется в формате с плавающей точкой. Символы %d и %f называются спецификацией формата. Функция printf, встретив в первом аргументе /?-ю спецификацию формата, выбирает для форматирования и вывода свой (k + 1) -й аргумент. В заключение мы хотели бы отметить, что возмож- ности функции printf значительно шире, чем описано здесь. 4.7. ОПЕРАЦИИ УВЕЛИЧЕНИЯ И УМЕНЬШЕНИЯ Приведенная в последнем примере программа на языке программирова- ния Си написана почти так же, как ее написал бы программист, в совер- шенстве знающий язык программирования Си. Для того чтобы довести Понятие "указатель" вводится несколько позднее. (Прим, ред.) 89
эту программу до совершенства, осталось заменить в ней форму записи оператора присваивания i= i+1, в результате выполнения которого значе- ние переменной i увеличивается на 1, на более короткую. Кстати, обратите внимание, что присваивание в языке программирования Си обозначается символом =, а не символом :=. Операция увеличения чего-либо на 1 встре- чается в программировании настолько часто, что в языке программирова- ния Си для этой операции введена специальная нотация ++. Так, увеличение на 1 переменной i может быть записано в языке программирования Си следующим образом: i++. Аналогично операции увеличения в языке прог- раммирования Си вводится операция уменьшения на 1. Для операции уменьшения в языке программирования Си также введена специальная нотация----. Например, уменьшение на 1 переменной i может быть записано в языке программирования Си следующим образом: i---------. Используя введенные нотации, окончательно программу, приведенную в последнем примере, можно записать следующим образом: main(){ int i; for(i = 1; i < Ю; i + +) printf("%d %d\n",i,i*i); } Операция увеличения и операция уменьшения имеют две формы. Так, в результате выполнения оператора a=i++; переменной а присваивается значение переменной i, после чего значение последней увеличивается на 1. Альтернативная форма операции увеличения имеет вид a=++i; В результате выполнения этого оператора значение переменной i увели- чивается на 1, после чего полученное значение переменной i присваивается переменной а. Операция уменьшения имеет точно такие же формы. В ряде случаев безразлично, в какой из форм употребляются операции увеличения и уменьшения. Например, в последней части заголовка оператора цикла for вместо i++ можно было бы записать ++i. 4.8. УПРАВЛЕНИЕ ПОСЛЕДОВАТЕЛЬНОСТЬЮ ДЕЙСТВИЙ Язык программирования Си предоставляет Вам широкий набор опера- торов, позволяющих управлять последовательностью действий. Все эти операторы основаны на проверке истинности некоторого условия. Условие является значением некоторого выражения. Если значение этого выражения не равно 0, то условие считается истинным. Если значение этого выражения равно 0, то условие считается ложным. 90
В этой главе часто используется термин оператор, который, вообще го- воря, нуждается в строгом определении. Определение того, что такое оператор. Вы найдете в литературе из приведенного нами списка. Основные трудности у начинающих пользователей, как правило, вызывает употреб- ление символов {, } и символа ;. Мы предпочли набор примеров работаю- щих программ (на основании которого Вы можете сделать определенные выводы) формальному описанию, над которым Вам пришлось бы поломать голову. Тем не менее, если у Вас возникают сомнения по поводу правиль- ности написанного Вами фрагмента программы, единственный способ раз- решить все сомнения — это воспользоваться формальным описанием языка программирования Си. 4.8.1. ОПЕРАТОР ЦИКЛА for Вы уже немного знакомы с оператором цикла for. В обобщенной форме оператор цикла for выглядит следующим образом: for (выражение!; выражение2; выражениеЗ) оператор Выполнение оператора цикла for заключается в следующем. Сначала всего один раз вычисляется ''выражение!''. Затем до тех пор, пока "выра- жениеЗ" не равно 0, выполняется "оператор", вслед за выполнением кото- рого вычисляется "выражениеЗ". Как правило, "выражение!" используется для инициализации некоторым значением переменной цикла, "выраже- ние2" — для принятия решения о выходе из цикла, а "выражениеЗ" — для модификации значения переменной цикла после выполнения каждого шага цикла. В приведенных выше примерах это было именно так. ОХно, два или все три выражения в заголовке оператора цикла for могут отсутство- вать. Если отсутствует "выражение!", то перед входом в цикл не произ- водится никаких действий. Если отсутствует "выражениеЗ", то после выполнения каждого шага цикла не производится никаких действий. Если отсутствует "выражениеЗ", цикл является бесконечным, т. е. отсут- ствующее "выражениеЗ" считается всегда истинным. Выход из цикла в этом случае может быть осуществлен только с помощью специального оператора завершения. Попробуйте воспользоваться бесконечным циклом для определения максимального значения, которое может принимать целая переменная. Перед тем как вызвать на выполнение программу с бесконечным циклом, вспомните, что говорилось о специальном символе INTERRUPT в гл. 1. Следующий пример иллюстрирует использование бесконечного цикла для определения максимального значения, которое может принимать целая переменная: main () { int i; for (i=1; /* выражениеЗ отсутствует*/; i++) printf ("i=%d\ n",i) ; 91
Еще раз обратите внимание на то, что использование символов ; в заголов- ке оператора цикла for обязательно, для чего приводим еще один пример заголовка бесконечного цикла: for (;;) 4.8.2. ОПЕРАТОР цикла while В обобщенной форме оператор цикла while выглядит следующим обра- зом: while (выражение) оператор Выполнение оператора цикла while заключается в том, что до тех пор, пока "выражение" на равно 0, выполняется "оператор". Фрагмент прог- раммы, в котором используется оператор цикла for, можно переписать в функционально эквивалентный фрагмент программы, в котором исполь- зуется оператор цикла while. Следующий пример демонстрирует фраг- мент программы на языке программирования Си, в котором имеются два оператора цикла: оператор цикла for и оператор цикла while, которые функционально эквивалентны (хотя оба довольно бесполезны): for(i = 1; i < = 10; i + +){ а = a + i; b = a«a; /* те же действия с помощью оператора цикла while */ i = 1; while(i < = 10){ а = a + i; b - а*а; i+ +; } При выполнении оператора цикла while сначала производится проверка истинности условия (т. е. вычисляется "выражение"), а уже затем, если это условие истинно, выполняется тело цикла ("оператор"). Иногда бывает необходимо выполнить тело цикла хотя бы один раз независимо от усло- вия. Следующий оператор предоставляет Вам такую возможность. 4.8.3. ОПЕРАТОР ЦИКЛА do-while Для того чтобы обеспечить выполнение тела цикла по крайней мере один раз, необходимо воспользоваться оператором цикла do—while. В обобщенной форме оператор цикла do—while выглядит следующим образом: do оператор while (выражение); 92
Обратите особое внимание на символ ; в операторе цикла do—while. Ис- пользование оператора цикла do—while поясним на следующем примере: j=5; do { a=a+i; i=i*2; ] while(—закончить выполнение цикла, когда (—j) станет равно нулю */ Если между служебными словами do и while находится всего один оператор, то отпадает необходимость использования символов { и j , что иллюстрируется следующим примером: 1=7 do a=a+i; while!—i); /* закончить выполнение цикла, когда (—i) станет равно нулю */ 4.8.4. ОПЕРАТОР ПРОДОЛЖЕНИЯ Continue И ОПЕРАТОР ЗАВЕРШЕНИЯ break Оператор продолжения continue вызывает преждевременное заверше- ние выполнения текущего шага цикла и переход к следующему шагу цикла. Оператор завершения break вызывает преждевременное завершение выполнения оператора цикла. Однако осмысленно воспользоваться этими двумя операторами Вы не сможете до тех пор, пока не познакомитесь с условным оператором if, поскольку применение этих двух операторов без предварительной проверки некоторого условия ни к чему хорошему не приводит. Поэтому примеры использования этих двух операторов рас- сматриваются несколько позднее. 4.8.5. УСЛОВНЫЙ ОПЕРАТОР if Условный оператор if должен быть знаком каждому, кто использует современные процедурные языки программирования. В обобщенной форме условный оператор if выглядит следующим образом: if (выражение) оператор else оператор Часть условного оператора if, начинающаяся со служебного слова else, яв- ляется необязательной. Существует простой способ определить, к какому из вложенных условных операторов if относится необязательная часть условного оператора if, начинающаяся со служебного слова else. Если имеется необязательная часть условного оператора if, начинающаяся со слу- 93
жебного слова else, то она относится к ближайшему предшествующему ей служебному слову if, еще не связанному ни с одним служебным словом else. Лучше всего это показать на примере: if(a < : b) /* если а меньше b, to */ b = 0; /* присвоить b 0 */ if(a > b){ /* если а больше b, to */ i+ +; /* увеличить i на 1 */ b = 0; /* присвоить b 0 */ }else c+ +; /* иначе, увеличить с на 1 */ if(a + b < с) if(a > с) Ы—I-; else /* это else относится к 4-му if */ с+ +; else{ /* это else относится к 3-му if */ а = с; b = 0; } Следующий пример иллюстрирует использование оператора продолжения continue в программе на языке программирования Си, которая обеспе- чивает вывод последовательности цифр 12345789, пропуская цифру 6: main(){ int i; for(i =1; i < = 10; i+ + ){ if(i = = 6) /* проверка на равенство */ continue; printf("%d”,i); } } Использование оператора продолжения continue в этом примере довольно искусственно, так как на практике в подобных ситуациях обычно вместо того, чтобы использовать оператор продолжения continue, изменяют смысл сравнения на противоположный. К сожалению, предложить Вам хороший пример использования оператора продолжения continue пока еще невозможно. Этот оператор выполняется тогда, когда значение пере- менной i равно 6 и вызывает преждевременное завершение шага цикла, на котором значение переменной цикла равно 6, и переход к следующему шагу цикла. Приведенный ниже пример показывает, как за счет исполь- зования необязательной части оператора if, начинающейся со служебного слова else, в программе, приведенной в предыдущем примере, можно уменьшить число символов {и } : 94
main(){ int i; for(i = 1; i <10; i ++) if(i = = 6) continue; else printf("%d",i); } Замена оператора продолжения continue на оператор завершения break приведет к завершению выполнения оператора цикла на том шаге цикла, значение переменной цикла для которого равно 6. 4.8.6, ОПЕРАТОР-ПЕРЕКЛЮЧАТЕЛЬ switch Оператор-переключатель switch, имеющийся в языке программирования Си, функционально близок к имеющемуся в ряде языков программиро- вания оператору выбора case, но не идентичен ему, чем и обусловлено отличие в названиях этих операторов. Оператор-переключатель switch ис- пользуется для ветвления на произвольное число ветвей в зависимости от значения некоторого выражения. Приведенный ниже пример иллюстри- рует использование оператора-переключателя switch для определения того, является ли результат вычисления значения выражения четным чис- лом в диапазоне чисел от 0 до 10 включительно, нечетным числом в том же диапазоне или вообще не является числом из этого диапазона: switch(a + ЬН case 0: case 2: case 4: case 6: case 8: case 10; printf("Even\n"); break; case 1: case 3; case 5: case 7: case 9: printf("Odd\rT); break; default: printff’Out of range\n”); 95
При выполнении оператора-переключателя switch сначала вычисляется значение выражения1, стоящего в круглых скобках непосредственно после служебного слова switch, а затем получаемое значение используется для выбора одной из ветвей, задаваемых с помощью служебных слов case. Для выбора ветви вычисленное значение выражения сравнивается со значениями выражений, стоящих после служебных слов case. Для вы- полнения выбирается первая встреченная ветвь, для которой эти значения совпали. После того как требуемая ветвь выбрана, выполняются все опе- раторы из списка операторов, соответствующего выбранной ветви. После завершения выполнения этого списка операторов (если только последним оператором в списке на является оператор завершения break) будет вы- полнен список операторов, соответствующий следующей ветви, и так далее до тех пор, пока не будут исчерпаны все списки операторов, соответствую- щие оставшимся ветвям. В приведенном выше примере используется оператор завершения break, обеспечивающий выход из оператора-переклю- чателя switch, завершающегося символом } сразу же после того, как будет завершено выполнение списка операторов, соответствующего любой из ветвей. Если бы в приведенном выше примере отсутствовали операторы завер- шения break, то в случае, если значение выражения а + b является четным числом в диапазоне от 0 до 10, на терминал было бы выведено последо- вательно три сообщения: Even Odd Out of range А что, по Вашему мнению, будет выведено, если в приведенном выше при- мере отсутствуют операторы завершения break, а значение выражения а + b не попадает в диапазон от 0 до 10? Выражения, стоящие после служебных слов case, должны все иметь различные значения и быть целыми константами. Список операторов, стоящий после служебного слова default, будет выполнен в том случае, если значение выражения в круглых скобках непосредственно после слу- жебного слова switch не совпадает ни с одним из значений, стоящих после служебных слов case. Служебное слово default в операторе-переключателе switch может отсутствовать, т. е. является необязательным. В этом случае, если значение выражения в круглых скобках непосредственно после слу- жебного слова switch не совпадает ни с одним из значений, стоящих после служебных слов case, ни одна из ветвей оператора переключателя switch не будет выполнена. 1 Значение этого выражения всегда приводится к типу int. (Прим, ред.) 96
4.9. ВЫРАЖЕНИЯ Теперь настало время обсудить вопрос о выражениях, который ранее мы просто игнорировали, хотя выражения широко использовались нами в приведенных в этой главе примерах. Выражение — это формула для вы- числения значения. Значение выражения вычисляется путем выполнения операций над операндами в порядке, соответствующем приоритету опе- раций. Рассмотрим ряд операций, используемых в выражениях. 4.9.1. ОПЕРАЦИИ ОТНОШЕНИЯ И ОПЕРАЦИИ СРАВНЕНИЯ К операциям отношения относятся следующие операции: < меньше > больше < = меньше или равно > = больше или равно К операциям сравнения относятся следующие операции: == равно ! = не равно Операции отношения имеют более высокий приоритет, чем операции срав- нения. Если Вас интересуют примеры использования этих операций, то вернитесь к параграфам, в которых описываются операторы управления последовательностью действий. 4.9.2, БИТОВЫЕ ОПЕРАЦИИ Язык программирования Си позволяет рассматривать целые величины как последовательности бит, к которым Вы можете применять следующие операции, называемые битовыми операциями: & и I или 9 исключающее или « сдвиг влево » сдвиг вправо ~ не Ниже приведены примеры использования битовых операций: а = Ь & 7; /* сбросить в 0 все биты, кроме трех младших #/ а = b I 7; /* установить в 1 три младших бита */ а=ЬЛ 1; /* инвертировать младший бит */ а — Ь; /* инвертировать все биты */ а = b « 3; /* сдвиг влево на три бита */ а = b » 3; /* сдвиг вправо на три бита */ Будьте особенно осторожны при использовании операции "сдвиг вправо", поскольку значение старшего бита результата этой операции существенно , 97 4 Зак 1165
зависит от используемой Вами ЭВМ, так как на одних ЭВМ для выполнения этой операции применяется логический сдвиг (заполнение нулями слева), а на других ЭВМ — арифметический сдвиг (заполнениезнаковым битом1). 4.9.3. ЛОГИЧЕСКИЕ ОПЕРАЦИИ Для получения логических выражений используются логические опера- ции, к которым относятся логические операции И (&&), ИЛИ (II) и НЕ (!). Результатом выполнения логической операции И является 1, если оба операнда этой операции ненулевые, и 0 в противном случае. Результа- том выполнения логической операции ИЛИ является 1, если хотя бы один из Операндов этой операции ненулевой, и 0 в противном случае. Результа- том выполнения логической операции НЕ является 1, если операнд этой операции нулевой, и 0 в противном случае. При редактировании с помощью редактора текстов ed программ, написанных на языке программирования Си, в которых используется логическая операция И, не забывайте, что символ & имеет для редактора текстов ed специальное значение. Ниже приводятся примеры использования логических операций: if (a<b && Ь<с) /* если а меньше b И Ь меньше с */ if (a<b I I Ь<с) /* если а меньше b ИЛИ b меньше с */ if (!Ь) /* если НЕ b */ Логическая операция НЕ чаще всего используется для проверки состояния флагов. Предположим, что переменная off принимает значение "истина" (т. е. не 0), если что-то выключено, и значение "ложь" (т. е. 0), если что-то включено. Тогда для выполнения требуемых действий в случае, если это что-то включено, можно воспользоваться оператором if (!off). 4.10. ОПЕРАТОРЫ ПРИСВАИВАНИЯ 4.10.1. СОБСТВЕННО ОПЕРАТОРЫ ПРИСВАИВАНИЯ Простейшей формой оператора присваивания является оператор просто- го присваивания, в результате выполнения которого переменной присваи- вается значение некоторого выражения: п ерем ен н ая=в ы ражен ие; Выражение может состоять из произвольных комбинаций допустимых значений и допустимых операций над ними. В число допустимых операций входят операции отношения, операции сравнения, битовые операции и логические операции. До сих пор мы ничего не говорили об арифметических операциях, считая, что в приведенных выше примерах можно разобраться без дополнительных разъяснений на этот счет. Однако на случай, если Вы чего-либо не поняли 1 См. [ 6, 7]. 98
или хотите убедиться в том, что все поняли правильно, мы, пользуясь удобной возможностью, приводим ниже список арифметических операций: + сложение — вычитание * умножение / деление % получение остатка от деления Возможно, что Вы ранее не встречались с операцией получения остатка от деления. Операция получения остатка отделения используется точно таким же образом, как операция деления, но результатом ее выполнения является не частное, а остаток от деления. Операндами операции получения остатка от деления могут быть только целые величины. Следующий пример иллю- стрирует получение остатка от деления переменной х на переменную у1: х%у Ниже приводятся примеры использования оператора простого присваи- вания: а=Ь; а=Ь+с; а= (b+c) /d; a~e>f; а= (e>f &&c<d) +1; а=а«3; Пусть Вас не смущает, что переменной присваивается значение, являющееся результатом выполнения операции отношения или логической операции, поскольку значением каждой из этих операций является какое-то число. Особый интерес в приведенном выше примере представляет последняя строка- Выполнение оператора, содержащегося в этой строке, сводится к тому, что значение переменной а сдвигается на три бита влево, а затем полученное значение присваивается той же самой переменной а. В языке программирования Си имеется специальная краткая форма записи опера- торов присваивания, в которых некоторая переменная входит и в левую, и в правую части оператора присваивания. Используя эту форму записи, оператор присваивания а=а«3; можно переписать в виде а«=з; 1 Авторы не обсуждают вопроса о знаке результата операции получения остатка от деления, который возникает в случае отрицательности хотя бы одного из операн- дов. [Прим. ред.) С 99
Особенно удобна эта форма записи, если левая часть оператора присваива- ния является сложным выражением, поскольку в этих случаях велика вероятность того, что при ее повторении в правой части оператора присваи- вания Вы сделаете ошибку. Кроме того, эта форма записи оператора при- сваивания экономит время при вводе и редактировании программ, написан- ных на языке программирования Си, с помощью редактора текстов ed. Очень широко эта форма записи используется в операторах присваивания типа следующего: а+=2; /* увеличить значение переменной а на 2=t/ Ниже приводится список допустимых кратких форм записи оператора присваивания; + = -= .= /= % = <<= >>= &= л= | = Наконец, еще одно преимущество сокращенной формы записи оператора присваивания заключается в том, что ее использование улучшает качество объектного кода, генерируемого транслятором с языка программирования Си. И последнее. Поскольку при использовании краткой формы записи оператора присваивания значение левой части оператора присваивания вы- числяется только один раз, то Вы можете избавиться от ошибок, связанных с использованием в левой части оператора присваивания массивов или ука- зателей, к которым применяются операции уменьшения или увеличения, поскольку при использовании полной формы записи оператора присваива- ния операции уменьшения или увеличения будут проделаны дважды. 4.10.2. ЗНАЧЕНИЕ ОПЕРАТОРА ПРИСВАИВАНИЯ В языке программирования Си операторы присваивания имеют значения и могут использоваться в выражениях. Значением оператора присваивания является значение выражения, стоящего в его правой части. С одной сторо- ны, эта особенность языка программирования Си оказывается крайне по- лезной, поскольку в ряде случаев она способствует написанию коротких и понятных программ, чтение которых доставляет истинное удовольствие. С другой стороны, эта особенность языка программирования Си, будучи использована неправильно, приводит к появлению программ, понять ко- торые постороннему человеку почти не под силу. Ниже приводится пример, иллюстрирующий эту особенность языка программирования Си: /* * Пример использования того, что в языке программирования Си * оператор присваивания имеет значение. * Два фрагмента, приведенных в этом примере, являются * функционально эквивалентными. * / /* первый фрагмент */ а = (b = c + d)/(e = f+ д); /* второй фрагмент */ 100
b = c + d; e = f + g; a = b/e; В подобных случаях мы предпочитаем для большей ясности использовать форму записи, соответствующую второму фрагменту. В дальнейшем при использовании формы записи, соответствующей первому фрагменту, мы попытаемся указать те ситуации, в которых, по нашему мнению, это оп- равдано. 4.10.3. КОНСТАНТЫ В языке программирования Си константы записываются в основном так же, как в большинстве других языков программирования. Целые константы записываются в виде 1, 2, 3 и т. д. Константы с плавающей точ- кой записываются в виде 1.0, 2.0, 3.0 и так далее. Константы с плавающей точкой могут также быть записаны в научной нотации: 1е6. Особый класс констант составляют символьные константы, которые записываются в виде 'а'. Значение символьной константы — это целое число, являющееся пред- ставлением символа в некотором коде. Возможно, что Вам придется стол- кнуться с восьмеричными константами, которые выглядят так же, как и целые константы, но, в отличие от последних, всегда начинаются с 0. Таким образом, 11 — это целая константа, а 011 — это восьмеричная кон- станта, эквивалентная целой константе 9. Символьные константы могут задаваться с использованием восьмеричной нотации. Так, константа \011, являющаяся представлением символа tab в коде ASCII, эквивалентна символьной константе \t. 4.11. СТРУКТУРЫ ДАННЫХ В языке программирования Си предусмотрены средства для структуриро- вания данных, становящиеся просто незаменимыми после того, как Вы привыкнете к их использованию. В этой главе мы рассмотрим только массивы и структуры. 4.11.1. МАССИВЫ Каждый программист, использующий какой-либо из современных языков программирования высокого уровня, имеет понятие о том, что такое массив. Следующий пример иллюстрирует описание в языке прог- раммирования Си массива xyz, состоящего из 100 целых элементов, кото- рые доступны по именам xyz [ 0], xyz [1],..., xyz [ 99]: int xyz [I00J; Как Вы видите, диапазон значений используемых индексов начинается с нуля и заканчивается величиной, на 1 меньшей, чем размерность массива, что часто служит источником ошибок начинающих пользователей. Причина выбора столь необычной схемы индексации станет ясна при более глубоком изучении вопроса о том, что же собой представляет массив с точки зрения 101
языка программирования Си. А сейчас Вам остается только привыкнуть к схеме индексации, используемой в языке программирования Си, и быть крайне осторожными при работе с массивами, поскольку ни один из суще- ствующих трансляторов с языка программирования Си не обеспечивает контроля нарушений по доступу к массивам, связанных с выходом зна- чения индекса за границы допустимого диапазона. Многомерные массивы рассматриваются в языке программирования Си как массивы, состоящие, в свою очередь, из массивов, например: int xyz [3] [4] [5]; В приведенном выше примере описано три массива, каждый из которых состоит из четырех массивов, каждый из которых, в свою очередь, состоит из пяти целых элементов. Однако это является тонкостью, которая понадо- бится, только если Вы займетесь использованием различных трюков с тем, чтобы с помощью указателей ускорить доступ к элементам некоторого массива. Приведенный ниже пример иллюстрирует две формы записи оператора присваивания, в левой и правой частях которого стоит элемент многомерного массива: xyz [1] [2] [3] += 2; xyz [1] [2] [3] = xyz[1] [2] [3] +2; Использование краткой формы записи оператора присваивания в случае, если в левой и. правой частях оператора присваивания стоит элемент много- мерного массива, является более предпочтительным, поскольку при этом, с одной стороны, этот оператор выглядит более наглядно, а с другой сто- роны, запись оператора упрощается, что уменьшает вероятность ошибки. 4.11.2. СТРУКТУРЫ Использование массивов не всегда является идеальным способом реше- ния некоторой задачи. Часто удобнее применять структуры данных типа записей, имеющихся в языке программирования Паскаль, но отсутствую- щих в языке программирования Фортран. В языке программирования Си имеются структуры, аналогичные записям в языке программирования Паскаль, а также объединения, аналогичные записям с вариантами в языке программирования Паскаль. Объединения в этой главе обсуждаться не будут. Приведенный ниже пример иллюстрирует описание в языке программи- рования Си структуры abc, задаваемой шаблоном структуры xyz, содер- жащим три поля — целое поле ааа, символьное поле bbb и поле с плавающей точкой с удвоенной точностью ссс: struct xyz{ int ааа; char bbb; double ссс; }abc; 102
Доступ к полям этой структуры осуществляется по именам abc.aaa, abc. bbb и abc.ccc. Описанный ранее шаблон структуры может быть впоследствии исполь- зован для описания других структур, что иллюстрируется следующим примером: struct xyz si, s2, s3; В приведенном выше примере с помощью описанного ранее шаблона струк- туры xyz описаны три структуры: si, s2 и s3, идентичные описанной ранее структуре abc. Язык программирования Си позволяет использовать масси- вы структур и структуры, содержащие в качестве своих полей массивы. Если Вам уже приходилось сталкиваться со структурами данных, ана- логичными структурам в языке программирования Си, то у Вас, наверное, возникнет вопрос об указателях. Язык программирования Си позволяет использовать указатели, что является одной из его наиболее известных особенностей. Указатели в программах на языке программирования Си используются чрезвычайно широко. Давайте теперь рассмотрим указатели. 4.12. УКАЗАТЕЛИ Если Вы раньше никогда не имели дела с указателями, то Вам будет достаточно сложно понять, что же это такое. Однако следует проявить настойчивость, поскольку указатели чрезвычайно широко используются при программировании на языке программирования Си. Нам очень жаль тех, кто не сумеет овладеть этим материалом и будет лишен возможности использовать указатели. В языке программиров'ания Си указатели до своего использования должны быть описаны. При описании указателей необходимо указывать объекты, на которые они указывают, что иллюстрируется следующим примером: int i, *р; В приведенном выше примере описаны целая переменная i и указатель р на целую переменную. Указатель называется р, а не «р. Символ » говорит о том, что р — это указатель, а служебное слово int уточняет, что р — это указатель на целую переменную. Прежде чем использовать указатель. Вы должны убедиться в том, что он указывает на что-то. Давайте установим указатель р таким образом, чтобы он указывал на целую переменную i. Для этого воспользуемся операцией получения адреса объекта & (не путать с битовой операцией &), обеспечи- вающей получение адреса того объекта, к которому она была применена: р = &i; Результатом выполнения приведенного выше оператора присваивания является присваивание указателю р значения, равного адресу целой пере- менной i, после чего указатель р указывает на целую переменную i. Для ЮЗ
того чтобы получить доступ к объекту, на который указывает указатель, используется операция доступа к объекту по указателю * (не путать с опе- рацией умножения *), обеспечивающей получение объекта, на который указывает указатель. Это означает, что выполнение двух операторов р = &i; *р = 2; эквивалентно выполнению оператора i = 2; 4.12.1. ИСПОЛЬЗОВАНИЕ УКАЗАТЕЛЕЙ Попытка обучить кого-либо использованию указателей только с по- мощью некоторой "инструкции по использованию" по своим результатам будет близка к попытке обучить кого-либо езде на велосипеде с помощью "инструкции по использованию". Безусловно, приводимые ниже примеры несколько облегчат Вашу задачу, но, к сожалению, ограниченный объем книги не позволяет привести достаточное количество таких примеров. Поэтому мы предлагаем Вам попытаться воспользоваться любыми доступ- ными материалами по языку программирования Си и различными приме- рами программ, написанных на языке программирования Си, которые Вам удастся раздобыть. Несколько дополнительных замечаний, возможно, помогут Вам лучше понять приводимые ниже примеры. Использование указателей помогает в создании более эффективных программ, особенно в тех случаях, когда требуется последовательный доступ к элементам массивов или полям структур. Достаточно частой операцией является копирование одного массива в другой. Обычно эта операция осуществляется с помощью следую- щего фрагмента программы: int a[100],b[100],i; for(i = 0; i < 100; i+ +) a[i] b[i]; Используя указатели, этот фрагмент программы можно переписать следую- щим образом: int а[100],b[100],*ap,*bp; ар = а; Ьр =Ь; while(ap < &а[100]) *ар+ + = *Ьр+ +; Особый интерес во втором фрагменте программы представляют операторы присваивания ар = а и Ьр = Ь. Вы, наверное, ожидали увидеть в правых частях этих операторов присваивания адреса первых элементов массивов. Эти операторы присваивания имеют такой вид потому, что в языке прог- раммирования Си использование имени массива эквивалентно использова- 104
нию адреса (а не значения) первого элемента массива, т. е. а эквивалентно &а[ 0]. После инициализации указателей выполняется цикл, в котором один массив копируется в другой. Цикл выполняется до тех пор, пока указатель ар указывает на элементы массива а. Возможно, что у Вас возникнет вопрос, почему *ар++ не увеличивает содержимого того элемента массива, на который в данный момент указы- вает *ар. Этого не происходит потому, что приоритет операции увеличения выше, чем приоритет операции доступа к объекту по указателю. Если бы мы написали (*ар)++ вместо *ар++, то добились бы увеличения того эле- мента массива, на который в данный момент указывает «ар. При этом цикл превратился бы в бесконечный. Вам понятно почему? На первый взгляд вариант с использованием указателей кажется ничуть не лучше предыдущего. Тем не менее использование указателей часто поз- воляет добиться большей эффективности, особенно в тех случаях, когда объекты, на которые они указывают, занимают более одного машинного слова. Еще большего эффекта можно достичь в тех случаях, когда указа- тели указывают на структуры, поскольку ряд трансляторов с языка прог- раммирования Си запрещает производить присваивание структуры на структуру. Транслятор с языка программирования Си, имеющийся в нашей версии ОС UNIX, позволяет производить присваивание структуры на струк- туру. Если окажется, что в используемом Вами трансляторе запрещается производить присваивание структуры на структуру, то Вам придется осуще- ствлять эту операцию путем последовательного присваивания полей. Рассмотрим более подробно использование указателей при работе со структурами. В этом случае указатели просто незаменимы, поскольку с их помощью можно легко связать структуры в списки и столь же легко построить деревья из структур. Вспомним структуру, задаваемую шабло- ном xyz. Давайте опишем еще одну структуру, задаваемую шаблоном xyz, и указатель на эту структуру: struct xyz anothr, *р; /• инициализируем указатель */ р = &anothr Теперь, используя указатель, можно получить доступ к различным полям структуры, на которую он указывает. Доступ к различным полям структу- ры может быть осуществлен с помощью операции указатель —> следую- щим образом: р —> поле где р — указатель, а "поле" — поле структуры, к которому необходим доступ. Результат выполнения операции указатель имеет тот же тип, что и соответствующее поле структуры, в соответствии с тем, как оно было описано в шаблоне структуры. Так, результатом выполнения р—>ааа яв- ляется целое, а результатом выполнения р—>bbb — символьное. 105
Аналогично тому, как в одном из приведенных выше примеров после- довательный доступ к элементам массива целых осуществлялся по указа- телю с использованием операции увеличения, последовательный доступ к элементам массива структур также может быть осуществлен по указателю с использованием операции увеличения. Язык программирования Си устроен таким образом, что прибавление единицы к указателю приводит к тому, что указатель после этого указывает на следующий элемент массива, неза- висимо от того, каков этот элемент: целый, символьный или структура. При этом, однако, не производится проверка выхода за верхнюю границу массива. Проиллюстрируем использование указателей для организации односвяз- ного списка: / * массив структур #/ struct listel { double num—part; struct listel » point—part; J list] 100]; main]) { int i; struct listel «Ip; /«инициализация «/ for (i = 0; i < 100; I++) { list [ i] .num—part = i + 1 listli] .point—part = &list[i + 1]; /• отметить конец списка «/ list [99] .point-part = 0; /« проход по списку »/ for(lp = list; Ip! =0; lp= Ip - >point—part) printf("%f\n’',lp- >num —part); В приведенном выше примере описан массив из 100 структур, каждая из которых содержит два поля — поле с плавающей точкой с двойной точ- ностью и поле указателя на структуру. В цикле производятся присваивание значений 1.0, 2.0, . . . , 100.0 полям с плавающей точкой с двойной точ- ностью и присваивание адреса следующего элемента массива полям указа- телей. Затем с помощью указателя осуществляется просмотр всего списка для последовательного вывода на стандартный вывод значений всех полей с плавающей точкой с двойной точностью. Полю указателя структуры, являющейся последним элементом массива, присваивается значение 0. Это допустимо и очень удобно, В языке программирования Си гаранти- руется, что указатель, которому присвоено значение 0, не указывает ни на какой объект. 106
Перед тем как осуществить просмотр всего списка, указателю 1р при- сваивается адрес первого элемента массива. До тех пор пока значение указателя 1р не равно 0, производится вывод на стандартный вывод значе- ния поля с плавающей точкой с удвоенной точностью структуры, являю- щейся элементом массива, на который в данный момент указывает указа- тель 1р. Затем указателю 1р присваивается значение поля указателя струк- туры, являющейся элементом массива, на который в данный момент указы- вает указатель 1р, что обеспечивает переход к следующему элементу массива. Убедитесь в том, что Вам все ясно в приведенном выше примере. Время, потраченное на разбор этого примера, окупится сторицей, когда Вы присту- пите к написанию программ на языке программирования Си. Что произой- дет, если строка list [99] .point—part = 0; будет заменена на строку list [99] .point—part = list; и почему это будет именно так? 4.13. ФУНКЦИИ 4.13.1. ОПИСАНИЕ ФУНКЦИЙ Описание функции начинается с типа функции, имени функции, за кото- рым в скобках указываются аргументы функции. Затем следует описание аргументов функции. Наконец после всего этого следует тело функции. Тело функции начинается с описания локальных переменных. Возврат из функции осуществляется с помощью оператора return или по достиже- нии символа }, соответствующего самому первому символу в теле этой функции. В качестве примера рассмотрим функцию, возвращающую в ка- честве своего значения сумму своих аргументов: int sum(arg1 ,arg2) int arg1 ,arg2;{ int total; total = arg1 +arg2; return (total); } В приведенном выше примере функция sum имеет тип int. Это означает, что в качестве своего значения функция возвращает целое. На самом деле описание типа функции в данном случае можно опустить, так как в языке программирования Си, если описание типа функции опущено, то по умолчанию считается, что такая функция возвращает в качестве своего значения целое. Функция sum имеет два аргумента (arg1 иагд2), которые описаны как целые переменные. В этом случае описание аргумен- 107
тов также можно опустить, поскольку в языке программирования Си по умолчанию предполагается, что речь идет о целых переменных, до тех пор пока явно не будет сказано, что это не так. Функция sum использует опи- сываемую в ее теле локальную целую переменную total. Функция sum вычисляет сумму своих аргументов и возвращает ее в качестве своего значения. Приведенный ниже пример иллюстрирует исполь- зование функции sum в программе, причем необязательные описания опу- щены: main(){ int i,j; for(i = 1; i< 100; i+ +) for(j = 1; j< 100; j+ +) printf ("%d + %d = %d\n",i,j,sum(i,j)); } sum(a,b){ int total; total = a + b; return(total); } Использование целой переменной total в приведенном выше примере не является обязательным, поскольку можно просто написать return (а + Ь); Оператор return может не иметь аргумента: return; Употребление оператора return без аргумента приводит к тому, что осуще- ствляется возврат из функции, при этом значение, возвращаемое функцией, не определено. В том случае, если значение, возвращаемое функцией, не используется, обращение к функции аналогично обращению к процедуре в языке программирования Паскаль. Поэтому в языке программирования Си допустимы оба следующих обращения к функции sum: sum (х, у); и a =sum (х, у); В первом случае возвращаемое функцией sum значение просто не исполь- зуется. Локальные переменные при входе в функцию имеют неопределенные значения и не сохраняют своих значений в промежутках между двумя обращениями к функции. Вы всегда должны присваивать значения локаль- ным переменным до их использования. Глобальные переменные, в отличие от локальных переменных, доступны в любой точке программы и при вызове программы на выполнение инициализируются нулем, если только 108
Вы не инициализировали их явно другими значениями. Пример, приведен- ный в конце этой главы, иллюстрирует инициализацию глобальных пере- менных произвольными значениями. В языке программирования Си, если тип функции не указан, то по умол- чанию предполагается, что функция возвращает целое значение. Если функ- ция возвращает значение, не являющееся целым, то транслятор с языка программирования Си рассматривает это как ошибку. При использовании функций, возвращающих значения, не являющиеся целыми, их надо объя- вить в самом начале программы. Ниже приведен пример, иллюстрирующий использование функций, возвращающих значения, не являющиеся целыми: ^define TWOPI (3.141592-2) extern double sin(),cos(),sqrt(); double func(); main(){ double i; for(i = 0; i<TWOPI; i + = 0.1) printf("i = %f, f = %f\n'’,i,func(i)); double func(x) double x;{ return (sqrt(sin(x) +sin(x)»cos(x))); В приведенном выше примере вычисляется значение корня квадратного из sin (х) + sin (х) * cos (х) для значений х из диапазона от 0 до 2 с шагом 0,011. Первая строка в приведенном выше примере выглядит несколько странно. Тем не менее эта строка написана на языке программирования Си. Эта строка будет обработана препроцессором, который вызывается коман- дой сс перед вызовом собственно транслятора с языка программирования Си. Препроцессор распознает #define и запоминает, что было определено в этой строке. В данном случае было определено TWOPI. Затем, встретив где-либо в тексте программы TWOPI, препроцессор заменит TWOPI на значение 3.141592*2. Эта возможность широко используется для повыше- ния удобочитаемости программ и упрощения внесения изменений в них. Если все константы в программе определены таким образом, то для изме- нения значения некоторой константы достаточно изменить лишь одну строку программы. Строка extern double sin О,cos (),sqrt (); 1 Этот пример неудачен, так как sin (х| вычисляется дважды. Надо: sinlxl* (1 + coslxH. {Прим, ред,} 109
говорит о том, что в программе встретятся обращения к трем функциям sin, cos и sqrt, возвращающим значения с плавающей точкой с двойной точностью. Функции sin, cos и sqrt не описаны в этой программе, а опи- саны где-то в другом месте, поэтому используется описатель extern. Эти функции находятся в библиотеке. Сравните объявление функций и описа- ние функций и Вы увидите, чем они отличаются друг от друга. 4.13.2. ПЕРЕДАЧА ПАРАМЕТРОВ В ПРОГРАММЫ, НАПИСАННЫЕ НА ЯЗЫКЕ ПРОГ- РАММИРОВАНИЯ Си При вызове на выполнение программы, написанной на языке программи- рования Си, в ОС UNIX имеется возможность передать этой программе параметры. На самом деле, вызывая на выполнение команды Is и сс. Вы пе- редавали им параметры. Параметры, передаваемые программе, написан- ной на языке программирования Си, выступают в роли аргументов функ- ции main, что иллюстрируется следующим примером: main (argc, arqv) int argc; char **argv;{ В приведенном выше примере argc является целой переменной, a argv является указателем на указатель на символы. При вызове программы на выполнение значением целой переменной argc является число передавае- мых параметров, а указатель argv указывает на начало массива строк, являющихся передаваемыми параметрами. В приведенном ниже примере рассматривается программа, которая выводит переданные ей параметры на стандартный вывод: main (argc,argv) int argc; char**argv;{ int i; char *»cpp; cpp = arqv /* скопировать argv ♦/ for (i = 0; i< argc; i++) „ /* ДЛя каждого аргумента */ printf ("%s\n", *cpp++); /* напечатать его значение и перейти к следующему »/ { В приведенном выше примере символы %s говорят о том, что выводу подлежит строка символов. Разберите эту программу, а затем попробуйте выполнить ее. Эта программа реализует функции команды echo. Однако, выполнив программу, приведенную выше, Вы обнаружите, что результаты ее выполнения отличаются от результатов выполнения команды echo, потому что первое, что эта программа выводит на стандарт- ный вывод, так это имя файла, в котором она хранится (a.out, если Вы его не изменили), поскольку по установленному соглашению первым пара- метром, передаваемым программе, является имя файла, в котором хранит- 110
ся вызванная на выполнение программа. Некоторые программы исполь- зуют этот факт для того, чтобы определить последующие действия, приме- няя имя файла в качестве неявного флага. 4.13.3. АРГУМЕНТЫ ФУНКЦИЙ При обращении к функции аргументы обращения доступны внутри функции как обычные переменные. Однако если значения аргументов будут изменены внутри функции, то вне функции это изменение увидеть невозможно. Это происходит потому, что транслятор с языка программи- рования Си при обращении к функции осуществляет копирование указан- ных в обращении аргументов и передает в функции копии аргументов, а не сами аргументы. Однако часто бывает необходимо, чтобы функция изменяла значения переданных ей аргументов и возвращала измененные значения обратно. Для этого необходимо в качестве аргументов передать функции не сами объекты, а указатели на них: main(){ int i; i = 10; zero(&i) /* передать функции zero в качестве аргумента адрес целой переменной i */ /* т. е. указатель на i */ printf("i = %d\n'',i) /* напечатать значение целой пе- ременной i */ zero(arg) int *arg { •arg = 0 /# присвоить 0 целой переменной, на кото- j рую указывает указатель *агд •/ В приведенном выше примере функции zero в качестве параметра передает- ся указатель на целое, а в теле функции zero осуществляется присваивание 0 целой переменной, на которую указывает переданный в качестве аргу- мента этой функции указатель. 4.14. СТАНДАРТНАЯ БИБЛИОТЕКА ВВОДА-ВЫВОДА При разработке языка программирования Си были приложены большие усилия для того, чтобы написанные на языке программирования Си прог- раммы были мобильны. Значительная часть этих усилий была направлена на создание цельной и мобильной системы ввода-вывода. В результате была создана стандартная библиотека ввода-вывода, для краткости назы- ваемая далее SIO. Для того чтобы Вы могли использовать имеющиеся в 111
SIO функции в программе, необходимо в начале Вашей программы по- местить следующую обрабатываемую препроцессором строку: #include <stdio.h> Встретив эту строку и опознав #include, препроцессор осуществит подста- новку вместо нее в программу содержимого файла, хранящего все необ- ходимые для использования SIO объявления и описания, что позволяет Вам ни о чем больше не заботиться. В целом SIO слишком велика для того, чтобы описать ее в этой главе. Б. У. Керниган и Д. М. Ритчи проделали большую работу и описали SIO[ 9]. Вы найдете это описание в гл. 9 их книги1. А здесь мы приведем лишь несколько примеров использования SIO. 4.14.1. ВВОД И ВЫВОД К моменту начала своего выполнения программа, как правило, имеет три уже открытых для использования файла. Эти файлы открыты для прог- раммы интерпретатором команд shell и называются стандартным вводом, стандартным выводом и стандартным протоколом. Для того чтобы иметь возможность работать с этими файлами, так же как, впрочем, и с любыми другими файлами, используя средства, предоставляемые SIO, Вы должны знать для каждого из этих файлов "нечто", называемое указателем на файл. Указатель на файл на самом деле представляет собой указатель на некото- рый внутренний объект типа структура. Для того чтобы упростить Вам жизнь, SIO выносит на уровень пользова- теля три символических указателя на файл1 2. Символические указатели на файлы называются stdin, stdout и stderr. Прежде чем перейти к рассмот- рению использования истинных указателей на файлы, рассмотрим исполь- зование символических указателей на файлы. Для того чтобы ввести один символ, применяется библиотечная функ- ция getc: с = getc(stdin); В результате выполнения этой строки со стандартного ввода будет введен один символ и помещен в переменную с. Библиотечную функцию getc можно использовать с любым допустимым (истинным или символическим) указателем на файл. При попытке осуществить ввод после конца файла Вам будет возвращено специальное значение EOF, определяемое в SIO. Значе- ния, возвращаемые библиотечной функцией getc, являются целыми, а не символьными, поскольку в дополнение ко всем допустимым символам, входящим в используемый кодовый набор, необходимо обеспечить воз- 1 Это описание далеко не полно. {Прим, ред.) 2 Соответствие между символическими указателями на файлы и истинными указа- телями на файлы будет установлено в результате включения в начало Вашей програм- мы строки #include <stdio.h>. (Прим, ред.) 112
можность возвращения EOF в качестве значения библиотечной функции getc. Для того чтобы вывести один символ, применяется библиотечная функ- ция putc: putc(charac, stdout); В результате выполнения этой строки на стандартный вывод будет выведен один символ. Библиотечную функцию putc также можно использовать с любым допустимым (истинным или символическим) указателем на файл, например stderr. При использовании функции putc могут возникнуть разного рода неприятности, если Вы воспользуетесь указателем на подле- жащий выводу символ. Дело в том, что с большой степенью вероятности функция putc реализована в виде макроса — еще одного средства, предо- ставляемого препроцессором. В результате расширения макроса препро- цессором может случиться так, что charac будет использоваться более одного раза. В случае, если charac является простой символьной перемен- ной, никаких затруднений не возникает. Однако, если charac является указателем, используемым совместно с операцией увеличения, может оказаться, что операция увеличения будет выполнена более чем один раз. Поэтому мы рекомендуем Вам не употреблять операцию увеличения и опе- рацию уменьшения в аргументах функций SIO. Библиотечные функции getc и putc позволяют Вам осуществлять посим- вольный ввод-вывод. Проиллюстрируем использование посимвольного ввода-вывода на примере программы, осуществляющей посимвольное копирование стандартного ввода на стандартный вывод и подсчитывающей при этом число скопированных символов, которое выводится на стандарт- ный протокол: # include <stdio.h> main () { int с, count; /» вводить по одному символу со стандарного ввода, пока не встретится EOF */ while ((c=getc(stdin)) !=EOP){ count++; /*если не EOF, увеличить счетчик на 1 */ putc (с, stdout) /* вывести символ на стандартный вы- । вод*/ /* вывести на стандартный протокол число скопированных символов */ fprintf (stderr, "%d characters read\ n", count) Обратите внимание на то, что в заголовке цикла while значение оператора присваивания сравнивается со значением EOF. Этот прием очень часто ис- 113
пользуется при написании программ на языке программирования Си,' по- этому запомните его хорошенько. В приведенном выше примере произво- дится обращение к библиотечной функции fprintf, а не к уже знакомой Вам библиотечной функции printf. Библиотечная функция fprintf похо- жа на функцию printf. Единственное отличие заключается в том, что при обращении к библиотечной функции fprintf необходимо в качестве перво- го аргумента использовать указатель на файл. Библиотечная функция printf обеспечивает возможность осуществлять вывод только на стандарт- ный вывод. Попробуйте воспроизвести приведенный выше пример, переназначая стандартный ввод с терминала на файл, содержащий исходный текст этой программы. Следующий пример, в котором предполагается, что исходный текст приведенной выше программы хранится в файле zzl.c, иллюстрирует необходимые для этого действия и результат выполнения, программы: 5 сс zz1.с $ a.out < zzl.c # include <stdio.h> main 0{ } 165 characters read $ 4.14.2. ОТКРЫТИЕ ФАЙЛОВ И ЗАКРЫТИЕ ФАЙЛОВ Наигравшись вдоволь со стандартным вводом и стандартным выводом, Вы наверняка захотите научиться открывать и закрывать свои собственные файлы. Для того чтобы открыть файл, применяется библиотечная функция fopen. Аргументами при обращении к этой библиотечной функции являют- ся имя файла и режим открытия файла. Существует три режима открытия файла: г — чтение, w — запись и а — добавление, т. е. запись в файл только после последнего записанного в него байта. В качестве своего значения би- блиотечная функция fopen возвращает указатель на файл или NULL. Указа- тели на файлы помещаются в переменные специального типа FILE, описан- ного в SIO. Приведенный ниже пример иллюстрирует открытие файлов fred и bill и копирование первого файла во второй: # include <stadio.h> main 0 { FILE *fp,*|p; /*два указателя на файл */ int с; /* установка fp */ if ((fp = fopen("fred", "r")> = = NULL){ fprintf (stderr,"Can't open fred\ n");; exit(—1); /* завершить выполнение */ 114
I* установка bp */ if ((bp = fopen("bill", "w")) = = NULL){ fprintffstderr, "Can't open bili\n"); exit)—1); /* завершить выполнение */ while((c = getc(fp)) != EOF) putcfc, bp); exit (0); Обращение к функции1 exit приводит к немедленному завершению выпол- нения программы, причем гарантируется, что возврата из этой функции не будет. Аргумент, используемый при обращении к функции exit, являет- ся кодом завершения программы и может быть использован, например, интерпретатором команд shell. При нормальном завершении выполнения программы код завершения равен 0. Любое другое значение кода завер- шения свидетельствует о ненормальном завершении выполнения прог- раммы. В данном случае индикатором ненормального завершения выпол- нения программы является код завершения, равный. —1. Заключительные замечания касаются режимов открытия файлов, хотя их названия в значительной степени все объясняют. Использование г в качестве спецификации режима открытия файла приводит к тому, что открытый файл будет доступен только по чтению. Использование w в качестве спецификации режима открытия файла приводит к тому, что открытый файл будет доступен только по записи, при этом, если файл с указанным именем не существовал, то он будет создан. Использование а в качестве режима открытия файла приводит к тому, что открытый файл будет доступен только для добавления в него новой информации. 4.15. ЗАКЛЮЧЕНИЕ Итак, мы закончили рассмотрение языка программирования Си. Нам не удалось рассказать в этой главе даже трети того, что мы собирались. Но, к сожалению, у нас нет больше места для того, чтобы продолжить рассказ о языке программирования Си. Многих наверняка не удовлет- ворит наш выбор круга вопросов, затронутых в этой главе. На претензии такого сорта у нас есть только один ответ: "Попробуйте сами написать книгу, посвященную языку программирования Си, в которой Вы все сможете сделать по-своему". 1 На самом деле exit является системным вызовом. (Прим, ред.) 115
Для первоначального знакомства с языком программирования Си Вы получили вполне достаточно сведений. Теперь необходимо приобрести практический опыт. Поэтому пишите как можно больше различных прог- рамм. В дополнение к этому читайте другие книги, посвященные языку программирования Си. Кроме того, пытайтесь разобраться в попадающих- ся Вам листингах чужих программ, написанных на языке программирова- ния Си. В качестве заключительного примера мы предлагаем Вам программу ovp, написанную М. Ф. Банаханом и приведенную в приложении Е. Эта программа осуществляет ввод со стандартного ввода или из указанных ей при вызове на выполнение файлов и осуществляет вывод на стандарт- ный вывод. В процессе своей работы программа ovp осуществляет пере- кодировку таким образом, что подчеркнутые символы на печати будут отображены более жирным шрифтом за счет печати с наложением. Некото- рое время эта программа использовалась в Брэдфордском университете и не содержит известных нам ошибок. В программе ovp используются регистровые переменные, не упоминав- шиеся нами ранее. Они отличаются от целых переменных тем, что не имеют адреса, поэтому для работы с ними нельзя использовать указатели. Опи- сание переменной как регистровой является указанием (только лишь ука- занием) транслятору с языка программирования Си о том, что эта пере- менная будет активно использоваться и поэтому было бы желатель- н о разместить ее в одном из регистров ЭВМ. Число переменных, которые действительно будут размещены в регистрах ЭВМ, зависит от типа исполь- зуемой ЭВМ. Для ЭВМ семейства PDP-11 это число равно трем. Необходимо помнить, что использование регистровых переменных без необходимости может привести к противоположным результатам, поскольку неверно ориентирует транслятор с языка программирования Си. ГЛАВА 5. ФАЙЛОВАЯ СИСТЕМА ОС UNIX Файловая система ОС UNIX имеет иерархическую структуру, образую- щую дерево каталогов и файлов. Каждая файловая система (обычно это отдельный съемный пакет дисков) является независимо организованной и имеет корневой каталог, который, в свою очередь, содержит некоторое число файлов, которые могут быть как обычными файлами, так и ката- логами или специальными файлами. 5.1. КАТАЛОГИ С точки зрения ОС UNIX каталоги являются самыми обычными фай- лами, за исключением того, что по вполне понятной причине ядро ОС UNIX не позволяет никому (кроме себя самого) осуществлять запись в них. Было бы глупо разрешить запись в каталог всем без разбора, посколь- 116
Рис. 2. Каталог ку это привело бы к тому, что поль- зователи смогли бы нарушать целост- ность файловой системы как по зло- му умыслу, так и случайно, в резуль- тате каких-либо ошибок. Собственно имена файлов хранят- ся именно в каталогах. Каждый эле- мент каталога или, иначе говоря, вход в каталог содержит одно имя файла. Каждый пользователь ОС UNIX, как 8ы уже знаете, имеет основной каталог пользователя, который становится текущим каталогом непосредст- венно после входа пользователя в ОС UNIX. В своем основном каталоге пользователя Вы можете создавать новые каталоги, чтобы помещать в них те файлы, которые желательно сгруппировать вместе. Целый ряд каталогов ОС UNIX использует для своих нужд. Одним из них является корневой ка- талог. Местоположение каждого файла в файловой системе OCUNIXoaho- значно определяется путем по дереву: от корневого каталога (корня иерар- хической древовидной структуры) по цепочке, образованной иерархией ка- талогов, до этого файла. Другие каталоги, используемые самой ОС UNIX, содержат файлы, предназначенные для общего использования. Например, команды ОС UNIX хранятся в основном каталоге /bin, а библиотеки (архив- ные файлы) — в каталоге/lib. Каждый элемент каталога (вход в каталог) состоит из двух полей: поля, содержащего имя файла, которое, как Вы наверное помните, может состоять не более чем из 14 символов, и поля, содержащего указатель на "нечто". 8 терминологии ОС UNIX этот указатель называется ссылкой. Ссылка является указателем на нечто, называемое описателем файла, в котором хранится вся информация о файле: дата его создания, имя владель- ца, код защиты файла и прочая важная информация. Таким образом, на самом деле файл существует не в каком-то отдельно взятом каталоге, а как бы отдельно от него, поскольку элемент каталога (вход в каталог) содержит кроме имени файла только ссылку на описатель файла, а в по- следнем уже хранится вся информация о файле, как это показано на рис. 2, В любом каталоге содержится по крайней мере два элемента (входа), содержащие в поле имени файла имена . и . . . Элемент каталога (вход в каталог), содержащий в поле имени файла содержит ссылку на описатель файла, в котором хранится информация о самом каталоге. Это необходимо для того, чтобы любая программа (например, команда Is) могла прово- дить чтение информации из текущего каталога, не зная абсолютного полно- го имени файла. Таким образов, команда 117
эквивалентна команде Is . Элемент каталога (вход в каталог), содержащий в поле имени файла . . , содержит ссылку на описатель файла, в котором хранится информация о родительском каталоге текущего каталога, т. е. информация о каталоге, некоторый элемент которого в поле имени файла содержит имя текущего каталога. Таким образом, элемент каталога (вход в каталог), содержащий в поле имени файла имя . . , обеспечивает возможность перемещения по де- реву от текущего каталога на один шаг (уровень) в сторону корня дерева. Если Вас что-то смущает в этом объяснении, то вернитесь к гл. 2 и взгляни- те на рис. 1. 5.2. ПОЛНЫЕ ИМЕНА ФАЙЛОВ Путь от файла по иерархии каталогов называется в ОС UNIX полным именем файла. Полное имя файла, как Вы уже знаете, является слоговым. Каждый слог в полном имени файла, кроме, быть может, последнего слога, представляет собой имя файла, являющегося каталогом. Слоги отделяются друг от друга с помощью символа /. Последний слог в полном имени файла задает собственно имя файла, который может быть каталогом, обычным файлом или специальным файлом. Если полное имя файла начинается с символа /, то оно называется абсолютным полным именем файла. При ука- зании абсолютного полного имени файла поиск файла начинается в корне- вом каталоге: Например, использование абсолютного полного имени файла /dir1/dir2/dir3/f ilename приведет к тому, что в корневом каталоге будет найден элемент каталога (вход в каталог), содержащий в поле имени файла dir1, затем в каталоге dir1 — элемент каталога (вход в каталог), содержащий в поле имени файла dir2, затем в каталоге dir2 — элемент каталога (вход в каталог), содержащий в поле имени файла d 1гЗ, а затем в каталоге dir3 - элемент каталога (вход в каталог), содержащий в поле имени файла filename1. Если полное имя файла начинается не с символа /, то в этом случае оно называется относительным полным именем файла. При указании относи- тельного полного имени файла поиск файла начинается в текущем каталоге. Обычной практикой при работе в ОС UNIX является смена текущего каталога для уменьшения длины относительных полных имен файлов. 5.3. ОПИСАТЕЛИ ФАЙЛОВ Каждому файлу в ОС UNIX соответствует только один описатель файла. В рамках каждой файловой системы описатели файлов пронумерованы последовательно, начиная с 1. Номер описателя файла хранится в поле 1 Эта последовательность действий будет иметь место, если файл с таким абсолют- ным полным именем существует. (Прим, ред) 118
ссылки в элементе каталога (вход в каталог}, содержащем в поле имени файла то имя файла, по которому обеспечивается доступ к нему. При такой схеме взаимно-однозначного соответствия между именем файла и самим файлом нет. Несколько каталогов могут содержать элементы (входы в каталог), содержащие в поле имени файла различные имена файлов, а в попе ссыпки — номер одного и того же описателя файла 1 . Элементы каталогов (входы в каталоги), обеспечивающие доступ к одно- му и тому же файлу, называются ссылками на файл. Наличие такого меха- низма обеспечивает возможность доступа к одному и тому же файлу по различным абсолютным полным именам, соответствующим этому файлу. Таким образом, абсолютное полное имя файла — это способ привязки имени файла к описателю файла, причем последний может быть известен под различными именами. Описатели файлов содержат, как уже говорилось выше, информацию о файлах, а элементы каталогов (входы в каталоги) обеспечивают воз- можность доступа к файлам по их именам.. В описателях файлов содержит- ся следующая информация: Тип файла (обычный файл, каталог, специальный файл); Идентификатор владельца; Идентификатор группы2; Код защиты файла; Счетчик числа ссылок на файл; Дата создания файла; Дата последнего доступа и модификации; Указатели местоположения блоков файла на съемном пакете дисков. На рис. 2 показана часть каталога с четырьмя элементами, содержащими в поле имени файла соответственно имена файлов fred, jane, bill и coo. Файл 7 и файл 3 имеют только по одному соответствующему каждому из них входу в каталог. Соответствующие этим двум файлам входы в ка- талог содержат в поле имени файла fred и соо соответственно. Файл 2 имеет два соответствующих ему входа в каталог. Соответствующие этому файлу входы в каталог содержат в поле имени файла jane и bill соответ- ственно. Таким образом, можно говорить о том, что имена файлов jane и 1 Возможна также ситуация, когда файл доступен по разным именам в одном и том же каталоге. Именно такая ситуация показана, например, на рис. 2. {Прим, ред.} 1 Как Вы, наверное, помните, раньше речь шла об имени владельца и имени группы, а теперь в описателе файла вдруг откуда-то возникли идентификатор владельца и идентификатор группы. Дело в том, что для хранения имен владельца и группы тре- буется слишком много места (столь дорогого) в описателе файла. Поэтому в ОС UNIX кроме имен владельца и группы введены понятия идентификаторов владельца и группы. Идентификатор — это некоторое число, поставленное в соответствие имени. Для хранения каждого идентификатора в описателе файла требуется всего один байт. {Прим, ред.} 119
bill являются синонимами в смысле того, что по каждому из них обеспе- чивается доступ к одному и тому же файлу 2. Ссылки на один и тот же файл не обязательно должны располагаться в одном каталоге, а могут быть размещены произвольно по всей иерархии файловой системы. Един- ственным ограничением в этом смысле является требование того, чтобы сам файл и все ссыпки на него располагались на одном и том же съемном пакете дисков. Создать синоним для уже существующего имени файла можно с помощью команды In. Если Вы введете echo hello > jane In jane bill то создадите в текущем каталоге файл, доступ к которому может быть осуществлен как по имени jane, так и по имени bill. На рис. 3 показана небольшая файловая система, устроенная описанным выше образом. Обычно на практике используется более одного устройства внешней памяти. Позднее будет показано, как объединить отдельные фай- ловые системы, каждая из которых располагается на отдельном съемном пакете дисков, в единую файловую систему, распространяющуюся более чем на одно устройство внешней памяти. 5.4. ФИЗИЧЕСКАЯ СТРУКТУРА ФАЙЛОВОЙ СИСТЕМЫ ОС UNIX На рис. 4 показана физическая структура файловой системы ОС UNIX. С точки зрения ОС UNIX любая файловая система представляет собой последовательность блоков длиной по 512 байт каждый, пронумерованных последовательно, начиная с.0. Первый блок (с номером 0} зарезервирован для хранения промежуточного загрузчика, обеспечивающего, если это необ- Ссылка Рис. 3. Небольшая файловая система Номер блока 0 I 2+isize fsize блок, содержащий промежуточный загрузчик вулерблак i~list Блоки файлов, блоки косвенности и свобоВныв блоки возможно используется в качестве области для своппинга Рис. 4. Физическая структура файловой системы 120
ходимо, проведение начальной загрузки ОС UNIX. Обычно промежуточ- ный загрузчик является автономной программой (т. е. программой, кото- рая выполняется не под управлением операционной системы), обеспечиваю- щей перемещение выполняемой программы из файла в оперативную память и передачу управления загруженной программе. Второй блок (с номером 1) является так называемым суперблоком файловой системы или, иначе говоря, описателем физической структуры файловой системы. Суперблок содержит информацию о физической струк- туре файловой системы, в частности о размере файловой системы, т. е. числе блоков, составляющих файловую систему (fsize), числе блоков, зарезервированных для хранения описателей файлов (isize), и указатель на заголовок списка свободных блоков1. Подробно структура суперблока описана в File System (V). Блоки, номера которых находятся в диапазоне от 2+isize до fsize, предназначены для хранения информации. Все свободные блоки связаны в единый список свободных блоков. Блоки косвенности, применяемые файловой системой ОС UNIX для обеспечения возможности использования файлов большого размера, также выбираются из числа блоков, номера которых находятся в диапазоне от 2+isize до fsize. Описатели файлов содержат лишь ограниченное число указателей местоположения блоков файла на съемном пакете дисков. Для файлов большого размера, т. е. для файлов, состоящих из большого числа блоков, описатель файла содержит указатели на блоки косвенности, которые, в свою очередь, содержат указа- тели местоположения блоков файла на съемном пакете дисков или указате- ли на блоки косвенности следующего уровня. Если число блоков, состав- ляющих файловую систему, хранящееся в суперблоке (fsize), меньше, чем реальное число блоков на устройстве внешней памяти, то оставшиеся блоки могут быть использованы в качестве области для своппинга. При выполнении операции открытия файла в ОС UNIX сначала по пол- ному имени файла (абсолютному или относительному) определяется эле- мент каталога, в котором в поле имени файла содержится собственно имя файла, для которого производится операция открытия файла. В найденном элементе каталога из поля ссылки на описатель файла извлекается порядко- вый номер описателя файла. Затем описатель файла с соответствующим но- мером перемещается со съемного пакета дисков в оперативную память, если до этого он там отсутствовал. При перемещении описателя файла со съемно- го пакета дисков в оперативную память он подвергается незначительным из- менениям. После этого, используя находящийся в оперативной памяти опи- сатель файла, определяется тип открываемого файла и проверяется, разре- шено ли открытие файла для типа доступа, указанного в операции открытия 1 Точнее будет сказать, что fsize является максимальным номером блока в дан- ной файловой системе, a isize — максимальным номером блока в области, отведен- ной для хранения описателей файлов. [Прим, ред.) 121
файла. Если файл, который требуется открыть, является обычным файлом или каталогом и открытие файла для указанного в операции открытия фай- ла типа доступа к этому файлу разрешено для того, кто пытается осуществить операцию открытия файла, то файл открывается. В результате открытия файла пользователь получает номер дескриптора файла или пользователь- ский дескриптор файла, который он впоследствии будет использовать в операциях чтения, записи и позиционирования вместо имени файла. 5.5. СПЕЦИАЛЬНЫЕ ФАЙЛЫ Вы, наверное, уже запомнили, что в ОС UNIX различаются файлы трех ти- пов: обычные файлы, каталоги и специальные файлы. Файлы, относящиеся к первым двум типам, уже обсуждались нами. Файлы, относящиеся к треть- ему типу, образуют одну из наиболее необычных возможностей, предостав- ляемых Вам ОС UNIX, и соответствуют внешним устройствам — термина- лам, АЦПУ, накопителям на магнитных лентах, накопителям на магнитных дисках, графопостроителям, оперативной памяти пользователя и даже оперативной памяти ядра ОС UNIX. Конечно, специальные файлы, соответст- вующие оперативной памяти ядра ОС UNIX и накопителям на магнитных дисках, используемым файловой системой ОС UNIX, защищены от несанк- ционированного доступа. Описатели файлов, являющихся специальными файлами, отличаются от описателей обычных файлов и каталогов и ссылаются на программы ядра ОС UNIX, обеспечивающие работу с соответствующими устройствами. Дос- туп к специальным файлам пользователя, имеющего на это право, осущест- вляется точно таким же образом, как к обычным файлам, с учетом лишь ограничений, накладываемых природой устройств. Так, не имеет никакого смысла попытка ввода информации из специального файла, соответству- ющего АЦПУ. По установленному соглашению специальные файлы размещены в ката- логе /dev, что, однако, не является ограничением, налагаемым ОС UNIX, так как, вообще говоря, специальные файлы могут быть расположены произ- вольно по всей иерархии файловой системы. Таким образом, команды cat plotfile>/dev/gp приведет к выводу содержимого файла plotfife из текущего каталога на графопостроитель, а ввод команды рг -4 /dev/mtO приведет к выводу на терминал в четыре колонки информации, введенной с накопителя на магнитной ленте номер 0. Такая эквивалентность файлов и внешних устройств является важной особенностью ОС UNIX. На самом деле любой источник или приемник ин- формации в ОС UNIX с точки зрения проведения операций ввода-вывода трактуется как файл. 122
5.6. ЕЩЕ РАЗ О МЕХАНИЗМЕ ЗАЩИТЫ ФАЙЛОВ В ФАЙЛОВОЙ СИСТЕМЕ ОС UNIX О механизме защиты файлов в файловой системе ОС UNIX мы уже рас- сказывали в гл. 2. Мы решили еще раз ненадолго вернуться к этой теме для того, чтобы объяснить, каким образом код защиты файла хранится в описа- теле файла. Для хранения кода защиты файла в описателе файла отведено одно 16-раз- рядное слово, каждый бит которого используется определенным образом1: 04000 — разрешение переустановить идентификатор пользователя при вызове файла на выполнение на идентификатор владельца, хранящийся в описателе файла; 02000 — разрешение переустановить идентификатор группы при вызове файла на выполнение на идентификатор группы, хранящийся в описателе файла; 01000— сохранить разделямый сегмент чистого кода в области для своппинга после завершения выполнения; 00400 — разрешение на доступ к файлу по чтению для владельца; 00200 — разрешение на доступ к файлу по записи для владельца; 00100 — разрешение на доступ к файлу по выполнению для владельца; 00040 — разрешение на доступ к файлу по чтению для членов группы; 00020 — разрешение на доступ к файлу по записи для членрв группы; 00010— разрешение на доступ к файлу по выполнению для членов группы; 00004 — разрешение на доступ к файлу по чтению для прочих пользова- телей; 00002 — разрешение на доступ к файлу по записи для прочих пользова- телей; 00001 — разрешение на доступ к файлу по выполнению для прочих пользователей. Зная смысл каждого бита в коде защиты файла, можно задать аргумент mode команды chmod в виде восьмеричной константы: $ Is -1 mycmd — rw —rw —rw— 1 andy 4529 Jan 28 14:37 mycmd $ chmod 755 mycmd $ Is -1 mycmd — rwxr — xr — x 1 andy 4529 Jan 28 14:38 mycmd Как Вы уже знаете, последовательность символов -rw-rw-rw-, выведенная на терминал в результате выполнения первой команды 1s, является некото- рым представлением кода защиты файла mycmd. Каждый символ в этой последовательности символов соответствует одному из битов в коде защиты 1 Авторы приводят описание кода защиты файла не полностью. (Прим, ред.) 123
файл^, хранящемся в описателе файла. Символ — означает, что соответст- вующий бит равен 0, а любой другой символ означает, что соответствующий бит равен 1. Если бы первым символом в этой последовательности символов был не символ — ,а символ d, то это, как Вы, наверное, помните, означало бы, что файл является каталогом1. Поскольку Вы не имеете возможности изменить значение этого бита, то для Вас он является только информационным. Пос- ледующие девять символов можно разбить на три группы по три символа в каждой. Первая группа из трех символов соответствует разрешениям по каждому из трех типов доступа для владельца, вторая — для членов группы, третья — для прочих пользователей. Первоначальное значение кода защиты файла mycmd, равное -rw-rw-rw—, можно представить (переходя к 16 битам) в виде двоичного числа 1000000110110110 или в виде восьмеричного числа 100666 Первая единица здесь означает, что файл является обычным файлом. После выполнения команды chmod значение кода защиты файла mycmd стало -rwxr-xr-x или в виде двоичного числа 1000000111101101 или в виде восьмеричного числа 100755 Ранее упоминались биты, входящие в код защиты файла, которые разрешают или запрещают переустановку идентификаторов владельца и группы при вы- зове файла на выполнение. Теперь настало время рассказать о них более под- робно. Как Вы знаете, вызов команды на выполнение приводит к созданию но- вого процесса. Каждый процесс имеет действующий и реальный идентифика- торы пользователя и группы (т. е. всего их четыре). Обычно значения дейст- вующих и реальных идентификаторов (пользователя или группы) совпадают и являются значением идентификатора (пользователя или группы) того, кто вызывает команду на выполнение. Установка битов, разрешающих переустановку идентификаторов пользователя и группы, приводит к тому, что при вызове команды на выполнение значения действующих идентифика- торов пользователя и группы для процесса, в рамках которого будет вы- 1 Первым символом в этой последовательности символов может быть также сим- вол b или символ с специфицирующие, что файл является специальным файлом. (Прим, ред.) 124
полниться команда, будут установлены равными идентификатору владель- ца и идентификатору группы файла, содержащего эту команду. В случае, если бит, разрешающий переустановку идентификатора владельца, установ- лен в 1 (т. е. переустановка идентификатора владельца разрешена), то узнать об этом можно с помощью команды 1s, которая в этом случае выводит для владельца вместо символа х, символ s, что иллюстрируется следующим при- мером: $ chmod 4711 mycmd $ Is -1 mycmd — rws----X----X 1 andy 4529 Jan 28 14:45 mycmd Значение кода защиты файла, равное —rws---х---х, можно представить в виде двоичного числа 1000100111001001 или в виде восьмеричного числа 104711 Аналогично, если бит, разрешающий переустановку идентификатора группы, установлен в 1 (т. е. переустановка идентификатора группы разрешена), узнать об этом можно с помощью команды 1s, которая в этом случае выво- дит вместо символа х для группы символ s. В коде защиты файла имеется бит, обеспечивающий возможность сохране- ния разделяемого сегмента чистого кода в области для своппинга после за- вершения выполнения. Установка этого бита в 1 приводит к тому, что после первого вызова на выполнение команды, хранящейся в файле, в описателе которого в коде защиты файла этот бит установлен в 1, и завершения ее вы- полнения разделяемый сегмент чистого кода, обеспечивающий выполнение этой команды, будет сохранен в области для своппинга. Разделяемый сег- мент чистого кода — это часть программы, содержащая только машинные инструкции и неизменяемые данные, причем машинные инструкции образу- зуют так называемый "чистый" код. Программы, использующие разделяемый сегмент чистого кода, могут одновременно использовать одну-единственную копию его, что экономит физическую память ( виртуальная память при этом не экономится). Сохра- нение разделяемого сегмента чистого кода в области для своппинга после завершения выполнения команды позволяет уменьшить время от момента ввода команды с терминала до момента начала выполнения этой команды для наиболее часто используемых команд (таких, например, как ed, cat, Is). Вре- мя вызова команды на выполнение уменьшается за счет того, что вместо чте- ния разделяемого сегмента чистого кода из файла в оперативную память пос- ледовательно блок за блоком производится считывание разделяемого сегмен- та чистого кода из области для своппинга в оперативную память, которое осуществляется за одно обращение к диску. Кроме того, экономия времени достигается за счет отсутствия целого ряда действий, связанных с запуском 125
на выполнение разделяемого сегмента чистого кода, уже считанного в опера- тивную память из области для своппинга. Возможность уменьшения времени вызова команды на выполнение яв- ляется большим достоинством, повышающим реактивность системы для наиболее часто используемых команд. Платить за уменьшение времени вызова команд на выполнение приходится тем, что наиболее часто используемые ко- манды занимают дефицитное место в области для своппинга. Решение о том, какие команды считать наиболее часто используемыми и применять ли пре- доставляемую Вам ОС UNIX возможность уменьшить время вызова этих команд на выполнение, должны принять Вы сами. В предыдущих двух абзацах речь шла о командах, но мы надеемся, что Вы поняли, что поскольку команды в ОС UNIX являются программами, то можно, если очень захотите, умень- шить время вызова на выполнение Ваших собственных программ. Установить в 1 бит, обеспечивающий сохранение разделяемого сегмента чистого кода в области для своппинга после завершения выполнения, имеет право только пользователь, зарегистрированный в ОС UNIX под именем root. В ОС UNIX выделяется пользователь, называемый привилегированным пользователем, чьи возможности намного превышают возможности рядово- го пользователя. В ОС UNIX привилегированный пользователь зарегистри- рован под именем root. Существуют два способа стать привилегированным пользователем. Первый способ заключается в том, чтобы войти в ОС UNIX под именем root, а второй — во вводе с терминала команды su,которая будет проинтерпретирована интерпретатором команд shell. В обоих случаях Вам придется вводить соответствующий пароль, который, как, мы надеемся, держит- ся в тайне. Для того чтобы напомнить привилегированному пользователю о его могуществе, промптером интерпретатора команд shell для привилеги- рованного пользователя является символ #, а не символ $. Механизм защиты файлов в файловой системе ОС UNIX бессилен перед привилегированным пользователем, который имеет право вводить информацию в любые файлы (кроме каталогов) и выводить информацию из любых файлов. Некоторые команды ОС UNIX должны выполняться в режиме привилеги- рованного пользователя. Одной из таких команд является команда passwd, которая позволяет пользователям осуществлять смену паролей. Информация о паролях хранится в файле /etc/passwd в закодированном виде. Владель- цем файла /etc/passwd является привилегированный пользователь, т. е. пользователь, зарегистрированный в ОС UNIX под именем root. Доступ по записи к файлу /etc/passwd разрешен только для владельца, все прочие ка- тегории пользователей имеют разрешение на доступ к этому файлу только почтению. Команда passwd хранится в файле /bin/passwd. Для того чтобы обеспечить выполнение команды passwd в режиме привилегированного поль- зователя, помимо того, что владельцем файла /Ып/passwd, является приви- легированный пользователь, в описателе файла /bin/passwd, в коде защиты файла бит, разрешающий переустановку идентификатора пользователя уста- новлен в 1. Это приводит к тому, что в результате вызова команды passwd 126
на выполнение действующим идентификатором пользователя того процесса, в рамках которого выполняется команда passwd, становится идентификатор, соответствующий пользователю, зарегистрированному в ОС UNIX под именем root, т. е. на время выполнения команды passwd рядовой пользователь стано- вится как бы привилегированным пользователем. Заканчивая рассказ о привилегированном пользователе, мы хотели бы только отметить, что если несколько пользователей, знающих пароль для вхо- да в ОС UNJX по имени root, попытаются одновременно с разных термина- лов войти в ОС UNIX под этим именем, то каждый из них успешно войдет в ОС UNIX и станет привилегированным пользователем (получит на термина- ле в качестве промптера интерпретатора команд shell символ #, а не символ $). Таким образом, ОС UNIX позволяет работать в ней одновременно нес- кольким привилегированным пользователям. 5.7. МНОГОТОМНЫЕ ФАЙЛОВЫЕ СИСТЕМЫ Хотя корневая файловая система всегда располагается на одном и том же (фиксированном) физическом устройстве, это не означает, что вся файловая система ограничивается только этим физическим устройством. Соответст- вующим образом может быть осуществлено подключение размеченного тома (обычно это съемный пакет дисков) к некоторому каталогу, уже существу- ющему в корневой файловой системе. Результатом такого подключения явля- ется то, что все обращения к каталогу, к которому подключена файловая сис- тема, будут трансформироваться в обращения к корневому каталогу подклю- ченной файловой системы. После подключения некоторой файловой системы к корневой файловой системе с точки зрения пользователя нет никакой раз- ницы между файлами, находящимися на корневой файловой системе, и фай- лами, находящимися на подключенной файловой системе. Для пользователя теперь существует единая файловая система иерархической древовидной структуры, в которой подключенная файловая система образует некоторое поддерево1. Подключение файловых систем не требует перезапуска ОС UNIX. Для повышения эффективности и безопасности работы мы рекомендуем Вам, если это возможно, выделить под рабочую область для временных фай- лов отдельный том (отдельную файловую систему) и подключать ее к катало- гу /tmp. Повышение эффективности в этом случае достигается за счет того, что поскольку каталог /tmp активно используется трансляторами и компо- новщиком, то добавление еще одного устройства может существенно ус- корить работу. Надежность в этом случае повышается за счет того, что пос- кольку каталог /tmp является одним из наиболее активно используемых каталогов, то при нарушении целостности файловой системы вероятность того, что нарушенными окажутся файлы этого каталога, очень высока. Если 1 Однако создание в каталоге, находящемся на корневой файловой системе, ссыл- ки на файл, находящийся на подключенной файловой системе, и наоборот, запрещено. (Прим. ред.) 127
каталог /tmp является отдельным томом, то восстановление целостности фай- ловой системы не представляет трудности, поскольку в каталоге /tmp нет никаких файлов, кроме временных, а следовательно, нет файлов, для кото- рых могло бы потребоваться восстанавливать информацию. Если устройство, на которое установлен том, отведенный под рабочую область для временных файлов, представлено специальным файлом /dev/ /гкО, то подключить файловую систему на этом томе к корневой файловой системе (к каталогу /tmp) можно с помощью команды /etc/mount /dev/rkl /tmp Вы должны запомнить, что команда mount находится в каталоге /etc, а не в каталоге /bin. Выполнять команды из каталога /etc рядовым пользова- телям обычно запрещено. Отключить ранее подключенную файловую систе- му можно с помощью команды /etc/umount /dev/rk1 В качестве примера многотомной файловой системы рассмотрим файло- вую систему, используемую нами самими. В этой файловой системе каталоги root, /bin, /етс, /lib и ряд других каталогов расположены на корневой фай- ловой системе. Файлы пользователей располагаются на отдельной файловой системе, подключаемой к каталогу /usr в корневой файловой системе на эта- пе запуска ОС UNIX. Под временную рабочую область для временных фай- лов выделена отдельная файловая система, располагающая на маленьком дис- ке с фиксированными головками и подключаемая к каталогу /tmp в кор- невой файловой системе. Перед подключением этой файловой системы осу- ществляется ее разметка, обеспечивающая удаление всех вре'менных файлов, которые остались там после последнего выключения ОС UNIX, так как их не успели удалить. Дополнительную информацию об использовании команд mount и umount Вы получите в гл. 10. 5.8. РАЗМЕР ФАЙЛОВ И ВОЗМОЖНОСТЬ МОНОПОЛЬНОЙ РАБОТЫ С ФАЙЛАМИ Размер файлов в ОС UNIX может расти практически безгранично, посколь- ку в ОС UNIX нет средств ограничения размеров файлов. Единственное, что ограничивает размер файлов в ОС UNIX, так это схема, обеспечивающая отображение логических блоков файла в физические блоки на устройстве внешней памяти. В некоторых версиях операционных систем, подобных ОС UNIX, введены средства, обеспечивающие ограничение размера файлов. Если в используемой Вами операционной системе Таких средств нет (а это можно выяснить в имеющейся у Вас документации), то, используя системный вызов write (2), можно увеличить размер любого файла до тех пор, пока для этого имеется место на съемном пакете дисков. Если место на съемном пакете дис- ков будет исчерпано, на операторский (консольный) терминал начнет посту- пать следующее сообщение: out of space on dev x/x 128
Единственный выход из такой ситуации — удалить файл, дальнейший рост которого невозможен. Чем скорее Вы удалите этот файл, тем лучше, так как в противном случае дальнейшая работа ОС UNIX станет невозможной в самое ближайшее время, особенно если этот файл расположен на корневой фай- ловой системе. Говоря о недостатках ОС UNIX, обычно отмечают невозможность моно- польной работы с файлами1, необходимой для сколько-нибудь серьезной работы с базами данных. Вообще говоря, общую схему защиты можно реа- лизовать на основе использования взаимодействующих процессов. Тем не менее если ОС UNIX будет широко применяться в режиме коммерческой эксплуатации, то необходимо реализовать механизм для возможности мо- нопольной работы с файлами внутри ОС UNIX. В некоторых операционных системах, подобных ОС UNIX, механизмы такого типа уже реализованы. Вопрос о наличии или отсутствии таких механизмов в используемой Вами операционной системе можно выяснить в имеющейся у Вас документации. 5.9. УПРАЖНЕНИЯ 1. Что такое полное имя файла? 2. Какова структура файловой системы ОС UNIX? 3. Что такое описатель файла? Чем отличаются описатели файлов для различных типов файлов? 4. Что такое специальный файл? 5. Каким образом следует модифицировать Вашу программу для того, чтобы обеспечить возможность работы со специальными файлами? Г Л А В А 6 . ИНСТРУМЕНТАЛЬНЫЕ СРЕДСТВА ОС UNIX 6.1. ЧТО ПРЕДСТАВЛЯЮТ СОБОЙ ИНСТРУМЕНТАЛЬНЫЕ СРЕДСТВА ОС UNIX Одним из несомненных достоинств ОС UNIX ’вляется та легкость, с кото- рой пользователь может применять, комбинировать и даже создавать собст- венные инструментальные средства. Что же такое инструментальные средст- ва? В простейшем определении инструментальное средство — это просто про- грамма. Любая программа, выполнение которой дает некоторый результат, может быть названа инструментальным средством (инструментальным сред- 1 * * * 5 1 Под возможностью монопольной работы с файлами понимается возможность по- лучения некоторым процессом исключительного права на доступ к файлу. Это озна- чает, что все остальные процессы, желающие работать с этим файлом, будут ожидать, пока первый процесс не закончит свою работу с этим файлом. Файлы, открытые в дан- ный момент для чтения, не могут быть открыты кем-либо еще для записи, а файлы, открытые в данный момент для записи, вообще не могут быть открыты кем-либо еще. См. [1], с. 86-88. 5 Зак И65 129
ством для получения этого результата). Понятно, что область применения такого инструментального средства определяется характером и возможнос- тями программы. Как и в любом ремесле, эффективность труда программис- та во многом зависит оттого, насколько богатым набором инструментальных средств он располагает, и того, насколько гибкими являются эти инструмен- тальные средства. Гибкость инструментальных средств играет при этом огром- ную роль. Итак, если Вы обзавелись хорошим набором инструментальных средств и перед Вами лежит обширное поле деятельности, можетесмело прис- тупать к работе. Если не вдаваться в подробности, то инструментальные средства, имеющи- еся в ОС UNIX, можно разделить на два типа. К первому типу относятся те инструментальные средства, которые пред- назначены для выполнения работ, специфических в самом общем смысле этого слова. К таким инструментальным средствам относятся, например, трансляторы с различных языков программирования. Инструментальные средства этого типа являются весьма жесткими, т. е. область их применения сильно ограничена. Это и понятно: нельзя ожидать, что транслятор с языка программирования Си станет с восторгом транслировать программы, напи- санные на языке программирования Бейсик. Ко второму типу относятся гибкие инструментальные средства. Инстру- ментальные средства, относящиеся ко второму типу, имеют, как правило, менее сложную структуру, нежели инструментальные средства, относящиеся к первому типу, и предназначены для решения широкого круга задач. Такие инструментальные средства имеют обычно несколько режимов работы. Вы- бор требуемого режима работы осуществляется с помощью флагов. Боль- шинство гибких инструментальных средств обладает одной важной особен- ностью — они обрабатывают текст, т. е. выполняют действия, понятные прос- тому смертному. И, наконец, последнее — гибкие инструментальные средства могут использоваться в качестве фильтров. 6.2. ФИЛЬТРЫ Важность фильтров невозможно переоценить, поэтому мы не станем изви- няться перед Вами за то, что остановимся на них подробнее. Любое инструмен- тальное средство может быть использовано двумя способами. При вызове нз выполнение того или иного инструментального средства Вы можете задать список аргументов, часть из которых будет, очевидно, флагами, специфици- рующими режим работы, которую предстоит выполнить инструментальному средству. Оставшаяся часть аргументов, будет по всей видимости, предс- тавлять собой список имен файлов. Инструментальное средство осуществит ввод из указанных файлов, обработает так или иначе их содержимое и выве- дет результаты своей работы. Инструментальное средство, вызванноеописан- ным выше способом, всегда выводит результаты своей работы на стандарт- ный вывод. При использовании того же инструментального средства другим спо- собом задания имен файлов в списке аргументов не требуется. Вместо этого 130
инструментальное средство будет осуществлять ввод со стандартного ввода, а результаты своей работы выводить на стандартный вывод. Теперь Вам дол- жно стать понятно, почему инструментальное средство, использованное та- ким способом называется фильтром. Когда инструментальное средство используется как фильтр, оно осущест- вляет ввод со стандартного ввода, обрабатывает введенную информацию и затем выводит результаты своей работы на стандартный вывод — это очень простая идея1. Однако полностью оценить все достоинства фильтров можно лишь при построении конвейеров с помощью канала связи одного инструмен- тального средства с другим. Канал — это средство, присущее только ОС UNIX; ни в одной из других известных нам операционных систем такого средства нет. Выбрав несколько стандартных инструментальных средств,поместив их име- на в одну командную строку вместе с флагами и разделив символом | (на- личие в командной строке символа | означает связывание команд, имена которых он разделяет, с помощью канала). Вы можете построить конвейер, который будет выполнять целый комплекс преобразований над входными данными. Очень часто для решения Вашей задачи окажется достаточным выб- рать подходящие инструментальные средства и скомбинировать их должным образом. Это избавит Вас от необходимости провести несколько мучительных часов за разработкой сложной и, возможно, неработоспособной программы. 6.3. СТАНДАРТНЫЙ НАБОР ИНСТРУМЕНТАЛЬНЫХ СРЕДСТВ ОС UNIX В этом параграфе мы приведем несколько примеров использования наибо- лее популярных инструментальных средств, входящих в состав ОС UNIX. Не имея места для подробного описания каждого инструментального средства, мы постараемся описать технику использования инструментальных средств, а также продемонстрируем Вам несколько "мастерских" приемов, которым мы научились, работая с ОС UNIX. 6.4. КОМАНДА рг Команда рг — это инструментальное средство для форматирования текста. Применение команды рг никак не модифицирует сам текст. Эта команда ис- пользуется для вывода текста на терминал или АЦПУ. Между прочим, АЦПУ — очень полезная вещь, и мы сочувствуем тем, кто его не имеет. Как и большинство инструментальных средств, команда рг имеет несколь- ко флагов, определяющих формат выводимого текста. В общем случае, когда команда рг применяется без флагов, это инструментальное средство работает следующим образом: осуществляет ввод текста из файлов, имена которых 1 Подобная трактовка фильтров отличается от той, которая принята авторами Руко- водства. По определению авторов Руководства фильтр — это программа, которая осу- ществляет ввод только со стандартного ввода, а вывод производит только на стандарт- ный вывод. Таким образом, любая программа, удовлетворяющая этому требованию, может быть названа фильтром, независимо от способа ее применения. (Прим, ред.) 131
перечислены в списке аргументов и выводит результаты своей работы на стандартный вывод, форматируя выводимый текст по страницам и помещая на первой строке каждой страницы номер страницы и текущую дату. Заме- тим, что команда рг предполагает, вообще говоря, вывод на АЦПУ, хотя стандартным выводом по-прежнему остается терминал. В связи с этим каждая страница выводимого текста форматируется так, как если бы текст выводился на АЦПУ, а именно: каждая страница состоит из 66 строк, первая строка содержит номер страницы и текущую дату, в начале страницы вслед за первой строкой, а также в конце страницы, команда рг помещает несколь- ко пустых строк. Если Вы вызовите на выполнение команду рг вовсе без ар- гументов или же список аргументов будет содержать только флаги, то коман- да рг осуществит ввод текста со стандартного ввода, а выведет сформатиро- ванный текст на стандартный вывод, т. е. будет работать в качестве фильтра. Начинающих пользователей ОС UNIX часто сбивает с толку безмолвная го- товность фильтров осуществлять ввод входной информации со стандартного ввода. Так, например, если Вы введете с терминала команду рг ОС UNIX как бы заснет. Что случилось? Ответ очень прост: Вы вызвали на выполнение команду рг без аргументов, следовательно, применили ее как фильтр, а потому она будет осуществлять ввод исходного текста со стандарт- ного ввода (им является Ваш терминал), а выводить сформатированный текст на стандартный вывод (им опять-таки является Ваш терминал). В такой ситуации было бы естественным потребовать, чтобы команда рг имела промп- тер, однако, если Вы переназначите стандартный вывод команды рг на АЦПУ, промптер команды рг появится в сформатированном тексте, что может ока- заться нежелательным, не говоря уже о том, что при замыкании стандартного вывода команды рг на стандартный вывод другой команды (при построении конвейера) последняя будет вынуждена обрабатывать и промптер команды рг, что далеко не всегда возможно. Попробуйте выйти из создавшегося положения, для чего введите с терми- нала произвольный текст, завершив его ввод нажатием клавиши, соответст- вующей специальному символу EOT. Не вздумайте нажать клавишу, соответ- ствующую специальному символу EOT, дважды, ибо если первое нажатие этой клавиши будет воспринято командой рг и приведет к корректному заверше- нию выполнения этой команды, то второе нажатие будет воспринято уже ин- терпретатором команд shell и приведет к завершению его работы, а это чре- вато для Вас необходимостью повторного входа в ОС UNIX. Рассмотрим пример использования команды рг: $ рг Have you seen the giant pistons on the mighty C.P.R. ? With the driving force of a thousand hourse -you know what pistons are. <EOT> Apr 8 11:30 1982 Pagel 132
Have you seen the giant pistons on the mighty C.P.R. ? With the driving force of a thousand horse - you know what pistons are. На самом деле, вслед за последней строкой текста в приведенном примере будут следовать пустые строки с таким расчетом, чтобы общее число выве- денных строк было равно 66. Чтобы воспользоваться командой рг не в качестве фильтра, введите с тер- минала вместе с именем команды список аргументов, среди которых должны быть имена файлов, содержимое которых Вы хотите вывести. Рассмотрим пример использования команды рг, который мы уже приводили в гл. 2, слегка изменив его: pr -h Test-Data tempfilel tempfile2 флаг —h в приведенном выше примере указывает на то, что следующий за ним аргумент Test—Data должен использоваться в качестве заголовка на каждой выводимой странице текста. Рассмотрим подробнее смысл некото- рых флагов команды рг. Если у Вас есть копия Руководства, сравните описа- ние флагов команды рг, приведенное в нем, с тем, которое дадим мы; это поможет Вам научиться пользоваться Руководством. Важной особенностью команды рг является возможность указания флагов внутри списка имен файлов.Такой способ использования флагов команды рг позволяет специфицировать отдельно формат каждого из выводимых тексто- вых файлов. Цапример, следующая команда: pr —h заголовок! file! —h заголовок2 file2 fiIe3 в результате своего выполнения выводит на терминал содержимое файла filel, озаглавив каждую страницу сформатированного текста "заголовок!", а затем выводит на терминал последовательно содержимое файлов file2 и file 3 соответственно, озаглавив каждую страницу сформатированного текс- та "заголовок2". Команда рг может выводить сформатированный текст в несколько коло- нок. Этой возможностью пользуются очень часто, она позволяет экономить время и бумагу, особенно если обстоятельства позволяют осуществить такой вид вывода. Представьте себе программу, которая выводит результат своей работы в виде длинного списка слов или чисел, состоящих из пяти-шести символов, по одному на строке. Переназначение стандартного вывода такой программы на печатающее устройство, работающее обычно на стандартной бумаге шириной в 132 позиции, является возмутительной расточительностью. Ярким примером такой программы может служить команда рг, примененная для вывода содержимого каталога, имеющего несколько сотен входов. Резуль- тат работы команды ргв этом случае будет представлять собой колонку слов, каждое из которых состоит максимум из 14 символов. 133
Однако разместить на странице больше восьми колонок текста Вам не удастся; это ограничение накладывается самой командой рг. Рассмотрим в качестве примера следующий конвейер: Is dirxxx | pr - 8 -w1 32 | Ipr В этом примере стандартный вывод команды Is замкнут на стандартный ввод команды рг с помощью канала. Флаг —8, входящий в список аргумен- тов команды рг, указывает на необходимость вывода сформатированного текста в восемь колонок на каждой странице, а флаг —w132 задает шири- ну страницы равной 132 символам. И, наконец, команда 1рг, стандартный ввод которой замкнут на стандартный вывод команды рг, осуществляет вы- вод на АЦПУ с применением механизма спуллинга1. Если Вы попробуете воспроизвести предыдущий пример в случае, когда выводимый каталог содержит мало входов, то получите, в результате, всего одну колонку. Дело в том, что,форматируя текст в несколько колонок, ко- манда рг заполняет их целиком по очереди слева направо и, следовательно, пока не будет заполнена первая колонка, не начнется заполнение второй. Организовать в такой ситуации вывод в несколько колонок можно только задав размер страницы меньше 66 строк. Такая возможность существует, ею можно воспользоваться, указав в списке аргументов команды ргфлаг —I. Например, следующий конвейер: Is | рг - 3 - w80 - И 5 в результате своего выполнения производит вывод текущего каталога в виде удобном для терминала дисплейного типа. Поясним смысл флагов командырг, использованных в этом примере: флаг —3 указывает на необхо- димость вывода в три колонки, флаг — w80 задает длину строки сформатиро- ванного текста равной 80 символам, а флаг —115 задает число строк на стра- нице сформатированного текста равным 15. Если текущий каталог содержит больше пяти входов, то выведенный командой рг на терминал текст будет содержать более одной' колонки. Это связано с тем, что 10 из 15 строк сфор- матированного текста отводятся командой рг на заголовок ипустыестроки в начале и конце страницы. Таким образом, собственно на текст останется всего пять строк на странице. Если Вам почему-либо мешают заголовок страницы и пустые строки в на- чале и конце страницы, от них можно избавиться, введя в список аргумен- тов команды рг флаг —t. Воспользуемся для иллюстрации сказанного пре- дыдущим примером: Is | рг -3 -t -w80 -115 1 Механизм спуллинга реализует принцип совместной работы нескольких переферий- ных устройств. Этот принцип обычно обозначают термином SPOOL (Simultaneous Peri- pheral Operations On-Line) — совместная работа периферийных устройств в режиме on- line. (Прим, ред.) 134
Мы надеемся, что Вы воспроизведете оба эти примера и если Ваш текущий каталог содержит более пяти входов, то в этом случае Вы сможете убедиться, что все описанное нами — правда. При выводе сформатированного текста в несколько колонок команда рг отделяет одну колонку от другой разумным количеством символов ш>, однако и здесь все в Ваших руках. Можете указать любой символ для отделе- ния одной колонки текста от другой с помощью флага —s. Например: Is | рг — 3 -w80 —115 -s, В приведенном примере колонки сформатированного текста будут отделе- ны друг от друга разумным количеством символов ,. И, наконец, разобраться с двумя последними флагами команды рг мы пре- доставляем Вам. Обратившись к Руководству, Вы без труда поймете, почему флаг —гл не может стоять в списке аргументов команды рг, используемой в качестве фильтра, а также к чему приведет появление в списке аргументов команды рг флага +7. Отметим только, что флаг+7 действительно начинается с символа +, а не с символа —, как обычно. Ну что ж, ограничение на вид первого символа флага основано на добровольных, а не на принудительных соглашениях, тем более, что обстоятельства л оз во л я ют нарушить его, так как флаги, включенные в список аргументов каждой команды ОС UNIX, интер- претируются самой командой. 6-5. ИМЕНА КОМАНД ОС UNIX Прервем ненадолго описание инструментальных средств ОС UNIX и обсу- дим имена команд ОС UNIX. Дело в том, что имена команд ОС UNIX весьма часто подвергаются критике и в некоторых операционных системах, подобных ОС UNIX, многие комнды переименованы. Выбор имен некоторых команд ОС U М1Ху(ействительно вызывает сомнение, и все-таки мы попробуем привести несколько аргументов в их пользу. Строго говоря, имя команды роли не играет, важно, какие действия эта команда выполняет. Было бы большой ошибкой перепутать имя некоторого предмета с его свойствами. Разве названия капусты или репы меняют их свойства или содержат в себе информацию о цвете их листьев и вкусе? Однако еще большей ошибкой было бы называть репой совершенно не похо- жие на репу вещи. Ситуации, когда одним названием обозначены несколько предметов, всегда чреваты путаницей. С одной стороны имена к о манд должны быть достаточно короткими — три, четыре или пять символов. Большинство пользователей ЭВМ не являются профессиональными машинистками, поэтому рассчитывать на то, что кто-ни- будь из них сможет ввести с терминала без ошибки десять символов подряд, не приходится. С другой стороны, имена,состоящие всего из одного символа, тоже неприемлемы — они слишком просты в написании, а имена некоторых "опасных" команд должны быть относительно длинными. 135
Наиболее популярны легкопроизносимые имена. Важно, чтобы имена ко- манд не совпадали со словами Вашего родного языка. Придание смысловой нагрузки именам команд — пустая трата времени; вместо помощи это, как правило, лишь вносит путаницу*. Прекрасным примером наименования команд является имя grep — корот- ко, легкопроизносимо, имеет одно-единственное значение и быстро входит в лексикон программиста. 6.6. КОМАНДЫ grep, fgrep, egrep Три очень простых инструментальных средства—grep, fgrep и egrep —при- меняются для отыскания в содержимом файлов некоторого набора символов. Во многих книгах подобные наборы символов называются регулярными вы- ражениями: этот термин имел, очевидно, какой-то смысл для тех, кто его при- думал2. Эти самые регулярные выражения — по сути дела наши старыедрузья- шаблоны, хорошо понятные редактору текстов ed и еще некоторым командам. Например, выполнение следующей команды: grep .abc file приводит к поиску и выводу на терминал тех строк файла file, которые со- держат контекст abc, следующийнепосредственноза произвольным символом. В результате могут быть найдены строки,содержащиеконтексты: аавс, хавс, .авс и т. д. В результате выполнения следующей команды: grep ' л xyz' file на терминал будут выведены все строки, начинающие с контекста xyz, содер- жащиеся в файле file. Список возможных комбинаций здесь бесконечен; за- метим только, что эти три инструментальных средства могут использоваться в качестве фильтров. Можно с полной уверенностью заявить, что команды, подобные команде grep, совершенно необходимы, особенно при использовании файлов для хра- нения некоторой справочной информации. Рассмотрим, например, следую- щую строку: grep $1 tel - dir 1 Читатель должно быть, уже заметил, что авторы весьма пристрастно относятся к идеологии ОС UNIX, отказываясь находить в ней какие бы то ни было недостатки. При- веденное здесь утверждение, на наш взгляд весьма спорное, является прекрасным до- казательством этой пристрастности. В действительности имена команд ОС UNIX в боль- шинстве случаев несут смысловую нагрузку, достаточно вспомнить такие из них, как рг, find, сс, рс и многие другие. (Прим, ред.) 1 Во время редактирования этой книги мы выяснили, что термин "регулярное вы- ражение” был введен математиком С. К. Клини и имеет вполне строгое определение. Мы надеемся, что читатель извинит нам наше невежество. 136
Поместите эту строку в командный файл phone и установите код защиты фай- ла таким, чтобы его можно было выполнять. Затем организуйте собственный телефонный справочник в файле tel—dir, который будет содержать, напри- мер, следующую информацию: mike 0274 733466 х8522 andy 0274 733466 х8515 Тогда, введя с терминала команду phone mike. Вы получите на терминале все строки файла tel—dir, содержащие шаблон mike. Совершенно аналогичным способом Вы сможете определить, кто является владельцем того или иного телефона. Различные варианты команды grep* 1 применяются в различных случаях, мы не станем обсуждать здесь этот вопрос. Вместе со своими флагами семейст- во команд поиска шаблона в текстовом файле представляет собой очень по- лезный набор инструментальных средств, и время, потраченное на их изуче- ние не пропадет даром. Какая конкретно команда из этого семейства Вам не- обходима, определяется стоящей перед Вами задачей, но последнее слово всегда за Вами. 6.7. КОМАНДА sort Команду sort часто незаслуженно обходят вниманием. Если перед Вами стоит одна из задач сортировки, у Вас есть шанс быстро справиться с ней, не прибегая к написанию специальной программы сортиров- ки на одном из языков программирования. Этот шанс дает Вам наличие в ОС UNIX такого инструментального средства, как команда sort. Приведем несколько коротких примеров, которые, надеемся, внушат Вам симпатию к этой команде. Вы, наверное, помните команду who, используемую для того, чтобы выяснить, кто из пользователей, зарегистрированных в ОС UNIX, рабо- тает в данный момент под ее управлением? Команда сортирует выводимую информацию, располагая имена терминалов в алфавитном порядке. Чтобы осуществить сортировку этой информации в алфавитном порядке, ориенти- руясь на имена пользователей, замкните стандартный вывод команды who на стандартный ввод команды sort, как это показано в следующем примере: who | sort Порядок следования строк выводимого списка будет зависеть, вообще гово- ря, от того, как кодируются на Вашей ЭВМ буквы и цифры. На большей час- ти современных ЭВМ для этой цели применяется код ASCII2, здесь все будет в порядке. А, вот бедняги, работающие на ЭВМ семейства IBM, получат по- тешный результат, ибо на ЭВМ этого семейства принят код EBCDIC5. 1 Авторы имеют в виду команды grep, fgrep и egrep. (Прим, ред.) г ASCII — Американский стандартный код обмена информацией. (Прим, ред.) i EBCDIC — расширенный двоично-десятичный код обмена информацией. (Прим, ред.) 137
Команда sort может производить сортировку строк текста как в алфавит- ном, так и в числовом порядке, в прямом или обратном направлении. Набор опций (option)1 команды sort достаточно богат, чтобы можно было выбрать необходимый Вам метод сортировки. Команда sort — это очень полезное инструментальное средство ОС UNIX, и мы настоятельно рекомендуем Вам изучить ее хорошенько, хотя бы для того, чтобы получить удовольствие, наблюдая, как тот, кто не пожелал воспользоваться Вашим советом и угро- бил три недели на решение сложной задачи, бьется головой о стену, убедив- шись, что его задача могла быть решена ровно три недели назад с помощью таких инструментальных средств ОС UNIXrKaK команды awk, fgrep, sort. 6.8. КОМАНДА awk Мы относительно редко используем команду awk в своей практике, ду- маем, что у Вас сложится примерно такая же ситуация. Многие пользователи ОС UNIX избегают пользоваться этим инструментальным средством, несмотря на его чрезвычайную гибкость, из-за его чрезмерной сложности. Команда awk представляет собой интерпретатор специального языка про- граммирования, позволяющего легко решать задачи контекстного поиска и преобразования текста. При этом команда awk использует более общий по сравнению с командами grep, egrep и fgrep подход и поэтому вполне может за- менить их, хотя и работает более медленно. Операции, которые команда awk в состоянии произвести с найденной строкой текста, могут быть намного сложнее, чем просто вывод этой строки текста. Так же как команда grep, команда awk, вызванная на выполнение, станет осуществлять ввод из одного или нескольких файлов либо со стандартного ввода и производить контекстный поиск строки, содержащей шаблон. Вы можете специфицировать несколько шаблонов и задать несколько различных операций, которые должны быть произведены с найденной строкой в случае удачного поиска. Набор шаблонов й указаний о необходимых операциях представляет собой программу на языке программирования awk. Програм- ма на языке программирования awk может быть введена с терминала в той же самой строке, что и имя команды awk, но чаще ее помещают в некоторый файл, а вызванная Вами на выполнение команда awk осуществляет ввод программы на языке программирования awk из этого файла. Программа на языке программирования awk состоит из расположенных на отдельных строках предложений следующего вида: шаблон {операция} Введя из очередного файла (или со стандартного ввода) очередную строку текста, команда awk осуществляет перебор в цикле указанных в программе на языке программирования awk шаблонов и контекстный поиск выбрзнно- ! Авторы здесь непоследовательны а терминологии, используя вместо термина "флаг" термин "опция" (Прим, рад.) 138
го шаблона в строке и в случае удачного завершения поиска выполняет соот- ветствующую операцию. И шаблон, и описание операции могут быть опуще- ны в предложении программы на языке программирования awk. В первом случае указанная операция будет произведена над каждой введенной строкой текста, а во втором случае команда awk произведет вывод каждой строки текста, содержащей шаблон, на стандартный вывод. Например, следующая тривиальная программа на языке программирования awk: { print } копирует стандартный ввод на стандартный вывод. Шаблоны в предложениях программы на языке программирования awk могут группироваться в арифметические и логические выражения, аналогич- ные тем, которые допускает синтаксис языка программирования Си1. Команда awk имеет несколько стандартных переменных (типа "строка символов"), задавая значения которых Вы можете ограничивать область кон- текстного поиска. Информация, вводимая командой awk, рассматривается как последовательность строк текста. Каждая введенная из входного файла или со стандартного ввода строка текста рассматривается командой awk как последовательность полей, разделителем которых по умолчанию является символ <_>. Число полей в последней введенной строке содержится в перемен- ной NF, а сами поля — в переменных $1, $2,. .. и т. д. Вся введенная строка текста содержится в переменной $0. И, наконец две последние перемен- ные — переменные RS и FS, первая из которых содержит разделитель строк, а вторая — разделитель полей. В качестве примера рассмотрим простую программу на языке программи- рования awk, которая выводит все строки входного файла, содержащие в первом поле более десяти символов и общее число таких строк: BEGIN { FS = а = 0 } length($1) > 10 { print length($.1) $0 а = а + 1 END { print "Total lines found = " a } Чтобы выполнить эту программу наязыкепрограммирования awk поместим ее текст в какой-нибудь файл, например в файл progfile, и затем введем с тер- минала следующую команду: awk -f orogfile [textfile ...] После этого команда awk начинает построчный ввод текста из специфициро- ванных файлов или со стандартного ввода, если они не специфицированы. Текст программы на языке программирования awk, однако можно ввести в одной строке с именем команды awk. Этот метод может быть полезен при 1 Подробное описание языка программирования awk можно найти в Руководстве. (Прим, ред.) 139
небольшом размере программы на языке программирования awk или в том случае, когда программы на языке программирования awk генерируются ин- терпретатором команд shell, который для этой цели выполняет подготовлен- ный Вами заранее командный файл. Предложения программы на языке программирования awk, содержащие служебные слова BEGIN и END, обрабатываются командой awk до и после ввода ею информации из входного файла или со стандартного ввода соответ- ственно. Таким образом, в приведенном выше примере задание вида раз- делителя полей в строках обрабатываемого текста и присвоение перемен- ной а значения 0 осуществляется до ввода командой awk исходной информа- ции. Внутренние переменные программы на языке программирования awk определяются по мере их инициализации; они могут иметь числовые или строчные значения. По умолчанию все внутренние переменные программы на языке программирования awk инициализируются числом 0 или, что то же са- мое нулевой строчной константой. Этот факт делает инициализацию перемен- ной а в приведенном выше примере излишней; мы ввели ее для большей наглядности. Шаблон, указанный во втором предложении программы на языке програм- мирования awk в приведенном выше примере, задает контекстный поиск строки текста, содержащей в первом поле более десяти символов; length — это встроенная функция языка программирования awk, которая в результате своего выполнения возвращает длину строки, указанной в скобках, в данном случае длину первого поля. Кроме функции length в языке программирова- ния awk имеется много других встроенных функций, например функция print. Все встроенные функции могут использоваться с аргументами, что иллюстри- руется следующим примером: length (41) или print ($2) Если встроенная функция языка программирования awk вызвана без аргу: ментов, то в качестве аргумента она будет использовать переменную $0; в таком случае функция length вернет количество символов в последней введенной строке текста. Итак, во втором предложении программы на языке программирования awk в приведенном выше примере указаны вывод количества символов в очеред- ной введенной строке текста и затем вывод всей этой строки на терминал. Третье же предложение в приведенном выше примере программы на языке программирования awk обеспечивает увеличение на 1 значения переменной а. Описанию команды awk можно посвятить целую главу; она представляет собой мощное и очень полезное инструментальное средство и относится к тем вещам, о которых говорят: "Что имеем не храним, потерявши плачем". 6.9. команда make В развитии любой системы программного обеспечения наступает в конце концов такой момент, когда ее структура становится слишком сложной для того, чтобы можно было удержать в голове логическую схему этой структу- 140
ры. В такой ситуации легко потерять логическую взаимосвязь между компо- нентами системы. Структура же системы может оказаться такой, что измене- ние одной из ее компонент приводит к необходимости перетранслировать не- которые другие ее компоненты или даже все остальные компоненты системы. Команда make предоставляет Вам возможность сохранить запись о подоб- ной взаимосвязи файлов и с ее помощью быстро и с минимальными затратами воспроизвести систему в целом после модификации отдельных ее компонент. Файл, в котором хранится запись о логической взаимосвязи файлов, называ- ют обычно файлом описаний. Команда make по умолчанию предполагает, что файл описаний имеет имя makefile; если же это не так, то при вызове ко- манды make на выполнение имя файла описаний должно быть указано в спис- ке ее аргументов. Попробуем разобраться в том, как работает команда make на следующем примере: пусть файл gron содержит некоторую выполняемую программу и логически связан с файлами main.c, gron.c, subs.s. Пусть далее файлы main.c и gron.c содержат исходные тексты программ, написанных на языке програм- мирования Си, а файл subs.s содержит исходный текст программы на языке Асемблера. Как Вы уже, наверное, догадались взаимосвязь файла gron с фай- лами main.c, gron.c и subs.s заключается в том, что он содержит выполняемый код программы, полученный в результате компиляции и компоновки прог- рамм, исходные тексты которых находятся в файлах main.c, gron.c, subs.s. Предположим далее, что файл main.c содержит кроме прочего строки //include "defs.h'' ^include "gron.h" а файл gron. с содержит кроме всего прочего строку //include "gron.h” Это означает, что файл main.c логически связан с файлами defs.h и gron.h,а файл gron.c логически связан с файлом gron.h. По установленному соглаше- нию объектный код программ, исходные тексты которых хранятся в фай- лах main.c,gron.с,и subs.s, необходимо поместить в файлы main.о, gron.о и subs.о соответственно. Компоновка этих объектных файлов с необходимыми функциями из библиотеки, хранящейся в файле /lib/lplot, даст Вам возмож- ность получить выполняемую программу, которая будет помещена в файл gron. Все описанные действия вместе с описаниями взаимосвязей перечислен- ных файлов в терминах команды make могут быть описаны следующим образом: gron: main.о gron.о subs.о сс main.о gron.о subs.o — (plot -о gron main.о: defs.h gron.h gron.о: gron.h 141
Поместите эти строки в файл описаний, например в файл makefile, и введите с терминала команду make Вы получите файл gron, который будет содержать выполняемый код нужной Вам программы. Рассмотрим подробнее содержимое полученного файла описаний. Первая строка содержит описание логической взаимосвязи файла gron с файлами, содержащими объектный код main.о gron.o subs.о Вторая строка представляет собой просто командную строку вызова на вы- полнение команды сс вместе со списком ее аргументов. Заметим сразу, что строки файла описаний, являющиеся командными строками, должны начи- наться с символа tab. Далее, третья строка файла описаний содержит описание логической взаимосвязи файла main.о с файлами defs.h gron.h И, наконец, четвертая его строка содержит описание логической взаимосвя- зи файла gron.o с файлом gron.h. Казалось бы логичным включить в содер- жимое файла описаний командные строки для получения файлов main.о и gron.o. Однако это излишне, и вот почему — команда make во время своего выполнения использует три источника информации: файл описаний, создаваемый пользователем; имена файлов и времена их последней модификации, взятые из фай- ловой ловой системы ОС UNIX; набор встроенных правил для восполнения некоторых пробелов в описании логической взаимосвязи файлов. Убедившись в том, что файлы main.c, main.o gron.c, gron.o subs.s, subs.о существуют, команда make с помощью набора встроенных правил сделает вполне достоверное предположение о том, что файл main.o можно получить в результате выполнения команды сс —с main.c файл gron.o можно получать в результате выполнения команды сс —с gron.c 142
а файл subs.о может быть получен в результате выполнения команды: сс —с subs.s1 Одной из важных особенностей команды make является предоставля- емая ею возможность задания макроопределений. Задать макроопределение можно двумя способами: во-первых, введя в файл описаний строку, содер- жащую символ =, например, следующим образом: OBJECTS = main.о gron.o subs.о во-вторых, с помощью аргументов команды make так, как это сделано ниже: make "LIBS = -Iplot - IS" Использование кавычек здесь необходимо, дабы избежать интерпретации символов и как разделителей аргументов команды make. Используя сделан- ные замечания, перепишем файл описаний, рассмотренный в предыдущем но- мере: OBJECTS = main.о gron.o subs.o LIBS = -Iplot gron: «(OBJECTS) cc «(OBJECTS) «(LIBS) — о gron main.o: defs.h gron.h gron.o: gron.h Сравнивая эту новую редакцию файла описаний с предыдущей, можно заме- тить, что здесь каждый контекст, совпадающий с правой частью макроопреде- ления, заменен на левую часть макроопределения, которая при этом заключена в скобки,перед которыми помещен символ $. Итак, файл описаний может содержать следующую информацию: макро- определения, описания взаимосвязей файлов, выполняемые команды ОС UNIX. J Вы можете облегчить жизнь команде make, переписав файл описаний следующим образом: gron: main.o gron.o subs.o cc main.o gron.o subs.o -Iplot -ogron main.o: main.c defs.h gron.h cc -c main.c gron.o: gron.c gron.h cc -c gron.c subs.o: subs.s cc —c subs.s Это избавит команду make от необходимости пользоваться набором встроенных правил. Кстати, запретить команде make использовать набор встроенных правил можно, помес- тив в списке ее аргументов флаг. — г. Кроме того, в таком виде файл описаний более понятен. (Прим, ред.} 143
Если Вам не удалось поместить в одну строку все, что Вы хотели, то вос- пользуйтесь символом \ для продолжения текста на другой строке. И в зак- лючение еще одно немаловажное замечание: набор команд ОС UNIX, постав- ленный в соответствие некоторой взаимосвязи файлов, будет вызван коман- дой make на выполнение лишь в том случае, если хотя бы один файл из семейства "источник" в описании этой взаимосвязи модифицирован позднее, чем был создан соответствующий файл из семейства "цель". Этим правилом команда make пользуется и в том случае, когда файл из семейства "цель" генерируется с помощью набора встроенных правил команды make1. Какой бы документацией, описывающей команду make, Вы не воспользо- вались, наверняка окажется, что понять изложенное весьма трудно. Это от- носится и к другим мощным инструментальным средствам ОС UNIX, Чтобы как следует понять принципы их работы и правила использования , не прихо- дится жалеть ни сил, ни времени. В такой ситуации может оказаться, что про- ще всего запастись несколькими готовыми файлами описаний и попробовать детально изучить их. 6.10. ЗАКЛЮЧЕНИЕ В этой главе мы обсудили несколько инструментальных средств ОС UNIX. Одной из причин популярности ОС UNIX является богатый набор инструмен- тальных средств, которым она располагает и'которые, на наш взгляд, впол- не заслуживают изучения. Мощность некоторых инструментальных средств внушает прямо-таки ужас. Каждый, кто хоть раз занимался проектированием систем программного обеспечения, использующих собственный формальный язык, по достоинству оценит такие инструментальные средства, как уасс и lex. Если перед Вами стоит сложная задача и Вы видите принципиальную воз- можность ее решения, воспользуйтесь инструментальными средствами ОС UNIX, и вместо рутинного программирования Вы испытаете радость быстрого и красивого решения Вашей проблемы. У подобных методов большое будущее. Изучите инструментальные средства ОС UNIX — не пожалеете! 1 * * * * * * В 1 Описание взаимосвязей файлов и выполняемые команды ОС UNIX могут храниться в одной строке файла описаний, в этом случае формат такой строки будет следующим: цель! [цель2 . . .] : [:] [источник! , . .] [; команды] [#. . .] Однако эту строку обычно разбивают на две, как это было сделано а рассмотренных примерах, и в этом случае формат будет следующим: цель! [цель2 . , .]; [:] [источник! . ..] [команды] [#...] В обоих случаях символ # означает начало текста комментария; наличие в строке од- ного символа : означает, что каждая из приведенных команд OCUNIX соответствует максимум одной из описанных взаимосвязей файлов, если же строка содержит последо- вательность символов : : — то каждой из описанных взаимосвязей файлов соответству- ют сразу все приведенные команды ОС UNIX. (Прим, ред.} 144
ГЛАВА 7. ПОДГОТОВКА ДОКУМЕНТАЦИИ В OCUNIX Нравится Вам это или нет, но огромная доля всех работ, связанных с прог- раммированием, приходится на документирование. Зачастую использовать программу, не сопровождаемую каким-либо описательным материалом, хотя бы в форме руководства по эксплуатации, просто невозможно. А кроме того, без подобного руководства по эксплуатации Ваши коллеги не смогут по дос- тоинству оценить всей важности Вашего вклада в науку. И, наконец. Вы должны быть уверены в том, что сами сможете воспользоваться созданным Вами инструментальным средством спустя несколько месяцев без повторного решения всей проблемы в целом. Подготовить простой, понятный и в то же время полный документ проще всего с помощью ЭВМ; только так Вы сможете без особых усилий шаг за шагом дополнять и видоизменять текст документа. Если Вы являетесь поль- зователем ОС UNIX, то Вам должно быть интересно, как подготовить доку- ментацию с помощью инструментальных средств, имеющихся в ОС UNIX. 7.1. ЧТО ТАКОЕ ПРОСТОЙ ТЕКСТ Первый и, пожалуй, самый ответственный шаг в подготовке текста Вы уже сделали — научились использовать редактор текстов ed. Как Вы уже зна- ете, ОС UNIX не различает файлы в зависимости от их содержимого. Файлы могут содержать исходные тексты программ, или же выполняемые програм- мы, или некоторый текст, например часть документа. Это означает, что с точки зрения редактора текстов ed содержимое файла не имеет никакого значе- ния — будет ли это исходный текст некоторой программы или текст некото- рого документа. В обоих случаях редактор текстов ed имеет дело с последо- вательностью символов. В простейшем случае для того, чтобы подготовить документ, достаточно создать файл и занести в него с помощью редактора текстов ed необходимый Вам текст. Это удобно для создания документов небольшого объема. Однако если Вы попытаетесь изучить какой-либо документ достаточно большого объема, то очень скоро убедитесь в том, что читать текст намного проще, если он сформатирован некоторым стандартным образом: левая и правая границы текста выровнены, текст сформатирован по стандартным страницам, каждая из которых имеет стандартный заголовок, и т. д. Использовать редактор текстов ed для выравнивания границ текста или постраничного его форматирования — занятие, по крайней мере, весьма уто- мительное, а добавление нескольких слов в стандартный заголовок страницы при условии, что часть текста уже сформатирована по страницам таким спо- собом, является крайне трудоемкой задачей. Решение подобных задач сущест- венно упрощает инструментальное средство, называемое форматором. Фор- матор представляет собой программу для форматирования текста стандарт- ным образом, что обеспечивает удобство чтения документов, подготовленных с его помощью. 145
7.2. ИСПОЛЬЗОВАНИЕ ФОРМАТОРА В действительности, в ОС UNIX имеется несколько форматоров. Какой из них выбрать, зависит от Вас, но мы предлагаем начать изучение форматоров с программы nroff (если, конечно, обстоятельства не заставят Вас поступить . иначе), в противном случае многие из приводимых нами здесь примеров мо- гут оказаться несостоятельными. Программа nroff представляет собой новую версию программы roff. Это удобное сокращение слова runoff, наиболее час- то используемого в качестве названия форматора в других операционных си- стемах, буква же п в слове nroff является сокращением слова new1. Раз уж Mil заговорили о различных форматорах, то отметим, что программа nroff имеет "сестру" по имени troff. В отличие от программы nroff, которая пред- назначена для форматирования текста и вывода его на АЦПУ, программа troff, предназначенная для форматирования текста и вывода его на фотона- борное устройство. Программа troff имеет по сравнению с программой nroff более широкие возможности. В дальнейшем мы не будем упоминать о про- грамме troff, так как ее описание не входит в наши планы, да и число пользо- вателей ОС UNIX, имеющих возможность использовать фотонаборное .уст- ройство, чрезвычайно мало. Программа nroff была разработана спустя несколько лет после того, как появилось несколько версий ОС UNIX, в состав которых вошли различные и несовместимые друг с другом версии программы roff. Адресуя эту главу но- вичкам, мы подобрали приводимые в ней примеры таким образом, что все они могут быть с одинаковым успехом воспроизведены под управлением любой версии ОС UNIX. Если же при воспроизведении того или иного примера у Вас возникнут трудности, то мы рекомендуем Вам обратиться за помощью к более опытному коллеге. Может оказаться, что в составе используемой Вами версии ОС UNIX имеется такая версия программы nroff, о существова- нии которой мы и не подозревали. В любом случае необходимо обзавестись ко- пией Руководства по использованию программы nroff, достоверно описыва- ющего имеющуюся у Вас версию программы nroff. Если Вы располагаете этим документом, то воздержитесь от немедленного изучения его, а прочтите сна- чала эту главу. 7.2.1. ВВЕДЕНИЕ В ФОРМАТИРОВАНИЕ Программа nroff производит ввод текста из файлов, имена которых пере- числены в списке ее аргументов, форматирует введенный текст и затем выво- дит результат своей работы на стандартный вывод. В общем случае программа nroff может быть вызвана на выполнение с по- мощью следующей команды: nroff [options] [file ...] 1 new — в переводе с английского "новый", (Прим, перев.) 146
Если в списке аргументов программы nroff нет имен файлов, то программа nroff будет осуществлять ввод со стандартного ввода и в этом случае ее мож- но использовать в качестве фильтра. Строки входных файлов подразделяются программой nroff на два типа: строки, содержащие директивы форматирования, и строки, содержащие текст, подлежащий форматированию. Строка, содержащая директиву форма- тирования, должна начинаться с символа . или с символа '• Вторую и третью позиции строки, содержащей директиву форматирования, должно занимать имя директивы форматирования. Директива форматирования, будучи вос- принята программой nroff, управляет режимом форматирования текста. Имя директивы форматирования состоит обычно из двух символов и является аб- бревиатурой фразы, описывающей действие этой директивы. Программа nroff разработана таким образом, чтобы ее использование было бы по возможности просто. Поэтому в результате обработки этой прог- раммой некоторого файла, содержащего текст, в котором отсутствуют строки, содержащие директивы форматирования. Вы тем не менее получите сформа- тированный некоторым образом текст. Мало вероятно, что Вас удовлетворит такой метод форматирования текста, однако сначала попробуйте воспользо- ваться им. 7.2.2. ТЕРМИНОЛОГИЯ ФОРМАТИРОВАНИЯ При описании процесса форматирования текста обычно используют тер- мины, редко применяемые где-либо еще. Мы попробуем объяснить,значение наиболее важных из этих терминов; это поможет Вам понять сам процесс форматирования текста. Мы уже говорили о том, какую важную роль при форматировании текста играют его левая и правая границы. Для обозначения положения левой грани- цы текста на листе бумаги используют термин величина отступа. Специально- го термина для обозначения положения правой границы текста на листе бу- маги нет, ибо положение на листе бумаги правой границы текста, очевидно, определяется величиной отступа и максимальной длиной строки текста. Программа nroff обрабатывает текст как последовательность слов, раз- деленных одним или несколькими символами i_>. Каждое слово рассматри- вается программой nroff как последовательность печатных символов, не со- держащая символов и. Допустим, что во введенном программой nroff тексте отсутствуют строки, содержащие директивы форматирования. В этом случае, форматируя введенный текст, программа nroff поместит в строке столько слов текста, сколько может поместиться между левой границей текста и правой границей текста. При этом, очевидно, левая граница текста окажется выровненной, правая граница текста также будет практически выровнена, а длина одной строки текста будет отличаться от длины другой строки текста не более чем на несколько символов. Подобное расположение слов в сформа- тированной строке текста обозначают термином полная строка, а процедуру 147
преобразования строки некоторого вида в полную строку называют обычно операцией заполнения строки. Частичное выравнивание правой границы текста достигается путем перено- са слов с использованием дефиса. Некоторые люди не любят читать текст, со- держащий ререносы слов, поэтому операцию переноса слов можно запретить. Если же операция переноса слов разрешена, то длины строк сформатирован- ного текста будут различаться на один, два или немногим более того символов. Для того чтобы добиться полного выравнивания правой границы текста, необходимо поместить последнее слово (если операция переноса разрешена, то часть последнего слова вместе с дефисом) в каждой полной строке непо- средственно перед правой границей текста. Однако при этом, очевидно, воз- никнет необходимость дополнить строку текста несколькими символами i_,1. Программа nroff проделывает такое дополнение, но распределяет "дополни- тельные”символы <_> по возможности равномерно между словами в строке текста. Описанная операция обозначается термином раздвижка строки. По- нятно, что строка, подвергнутая операции раздвижки строки, будет полной строкой, обратное же неверно. В общем случае программа nroff производит и операцию раздвижки строк, и операцию заполнения строк. Программа nroff рассчитана, вообще говоря, на форматирование текста по страницам и вывод сформатированного текста на АЦПУ. Однако результат форматирования представляет собой непрерывную последовательность строк текста. Для реального АЦПУ обычно используют бумагу с поперечной пер- форацией, так называемым фальцем' поэтому если Вы не хотите, чтобы фальц попал на страницу сформатированного текста, необходимо учесть это обстоя- тельство заранее. Типичным решением этой проблемы является помещение нескольких пустых строк или, что то же самое, нескольких символов linefeed в начале и в конце страницы сформатированного текста. Всю совокупность описанных выше операций обозначают термином постраничное формати- рование. Возможно, Вы замечали, что страницы многих книг в первых своих строках содержат некоторый специальный имеющий стандартную форму текст, или так называемый сквозной заголовок. Совершенно аналогично первым строкам последние строки всех страниц текста могут также содержать некоторый спе- циальный текст, имеющий стандартную форму, называемый сквозным колон- титулом. Совокупность описанных способов оформления страниц позволяет ввести еще один термин — сквозное оформление. Сквозное оформление тек- ста может выполняться программой nroff автоматически, что избавляет поль- зователя от необходимости самому заносить тексты сквозного оформления на каждую страницу подлежащего форматированию текста. 7.3. ДИРЕКТИВЫ ФОРМАТИРОВАНИЯ Если Вы воспользовались нашим советом и попробовали осуществить фор- матирование с помощью программы nroff содержимого некоторого файла, в котором отсутствуют строки, содержащие директивы форматирования, то 148
получили в результате текст с выровненными левой и правой границами, после чего у Вас, по-видимому, возникло желание сформатировать текст, содержавшийся в этом файле каким-нибудь другим, более интересным обра- зом. В таком случае, перейдем к рассмотрению некоторых директив форма- тирования. 7.3.1. ГЕНЕРИРОВАНИЕ РАЗРЫВОВ ФОРМАТА Разрывом формата называют прерывание операции заполнения строк текс- та и немедленный вывод полученных в результате этого неполных строк тек- ста. Образно говоря, разрыв формата вызывает немедленный "слив" заполняе- мой строки. Для получения разрыва формата можно применить соответству- ющую директиву форматирования или поместить в строке текст некоторого специального вида. Чтобы явно задать разрыв формата, воспользуйтесь сле- дующей директивой форматирования: ,br С другой стороны, к разрыву формата приведет включение в текст пустой стро- ки. И, наконец, разрыв формата может быть достигнут комбинацией несколь- ких других директив форматирования. При неявном указании разрыва фор- мата от может быть отменен введением в текст документа строки директивы форматирования, содержащей в первой позиции вместо символа . символ '. Ввиду столь высокой "сообразительности" программы nroff все пустые строки и все отступы в исходном тексте будут сохранены, даже если в исход- ном тексте отсутствуют строки, содержащие директивы форматирования. Чтобы пояснить сказанное, сделаем одно уточнение — появление в тексте строки, начинающейся с символа ш (в том числе и пустой строки), приведет к разрыву формата. 7.3.2. НЕСКОЛЬКО ПРИМЕРОВ Мы начнем с рассмотрения нескольких относительно простых директив фор- матирования, которые постараемся описать достаточно подробно. Следующая директива форматирования: .sp2 указывает программе nroff на необходимость пропустить в сформатирован- ном тексте две строки. Очевидно, что того же результата можно добиться, если поместить в соответствующем месте исходного текста две пустые стро- ки, но согласитесь,что это намного утомительнее. Возможно, Вы захотите выделить слово в строке текста, подчеркнув его. Рассмотрим в качестве примера следующий фрагмент текста: Это строка в которой . ul подчеркнуто одно слово. 149
После форматирования этого фрагмента текста с помощью программы nroff получим следующее: Это строка, в которой подчеркнуто одно слово. Здесь очень хорошо виден результат выполнения операции заполнения строк — исходный текст состоит из нескольких строк, а сформатированный текст состоит из одной строки. Если мы каким- либо способом отменим операцию заполнения строк, результат окажется следующим: Это строка, в которой подчеркнуто одно слово- Отметим разницу в применении директив форматирования . зри .□!. Первая из них применена с параметром 2, который помещен в той же строке, что и имя самой директивы форматирования. Параметром второй директивы форматирования является строка текста, следующая за строкой, содержа- щей имя этой директивы форматирования. Большинство директив форматирования функционирует аналогичным образом, одни из них вызывают временный эффект, например директива форматирования .ul, другие — постоянный, например директива форматиро- вания .nf. 7.3.3. ИЗМЕНЕНИЕ РАЗМЕРА СТРАНИЦЫ Положение текста на листе бумаги определяется тремя величинами: по- ложением левой и правой границ текста и длиной страницы, измеряемой чис- лом строк текста, помещенных на одной странице. Положение левой границы текста определяется величиной отступа. Задать величину отступа можно с помощью директивы форматирования .in, напри- мер: .in 10 \" устанавливает левую границу текста на позицию 10 .in +10 \" сдвигает левую границу текста ЕЩЕ на 10 позиций ВПРАВО .in —5 \'г сдвигает левую границу текста на 5 позиций ВЛЕВО Здесь необходимо отметить одно чрезвычайно важное обстоятельство — ди- ректива форматирования .in устанавливает положение левой границы текста до момента следующего появления в тексте имени этой директивы. В при- веденном примере первая строка содержит директиву форматирования, задающую абсолютную величину отступа или, что то же самое, абсолютное положение левой границы текста, в то время как две другие строки содержат директивы форматирования, которые изменяют значение величины отступа относительно текущего его значения, т. е. задают относительную величину отступа или относительное положение левой границы текста. Синтаксис мно- гих директив форматирования очень схож с синтаксисом директивы форма- тирования .in. 150
Приведенный выше пример можно рассматривать как фрагмент текста, состоящего лишь из строк, содержащих директивы форматирования, при этом последовательность символов \" означает начало комментария и указы- вает программе nroff на необходимость игнорировать все символы, располо- женные в строке справа от указанной последовательности символов. Иногда возникает необходимость изменить положение левой границы лишь для одной строки текста, например для первой строки нового параграфа. В этом случае можно воспользоваться директивой форматирования .ti, напри- мер: .ti 10 \" устанавливает левую границу следующей строки текста \" на позицию 10 .ti +4 \” сдвигает левую границу следующей строки текста на 4 пози- \" ции ВПРАВО .ti -6 \" сдвигает левую границу следующей строки текста на 6 пози- ций ВЛЕВО Все перечисленные в этом примере директивы форматирования воздейству- ют лишь на одну строку текста, а именно на строку, следующую непосредст- венно за строкой, содержащей директиву форматирования .ti. Все директивы форматирования могут объединяться друг с другом, для чего их необходимо расположить в последовательных строках, например: .in+8 \" сдвигает левую границу текста ЕЩЕ на 8 позиций ВПРАВО .ti +3 \" сдвигает левую границу следующей строки текста на 3 пози- \" ции ВПРАВО Приведенные в этом примере директивы форматирования позволяют удачно расположить текст нового параграфа некоторого документа, начав этот пара- граф с красной строки. Проиллюстрируем сказанное с помощью следующего фрагмента текста: Это конец текста внешнего уровня. .in +8 .ti +3 А это начало нового параграфа. Далее следует фрагмент текста, целиком расположенного на новом уровне — сдвинутого на 8 позиций вправо. .in —8 Возврат к внешнему уровню. После форматирования этого фрагмента текста с помощью программы nroff получим следующее: Это конец внешнего уровня. А это начало нового параграфа. 151
Далее следует фрагмент текста, целиком расположенного на новом уровне — сдвинутого на 8 позиций вправо. Возврат к внешнему уровню. В заключение отметим, что директива форматирования .in вызывает раз- рыв формата, именно поэтому контекст "Возврат к внешнему уровню." и не попал на предыдущую строку сформатированного фрагмента текста. Подумайте кстати, что произошло бы, если бы вместо директивы формати- рования .in—8 была применена директива форматирования 'in—8? Положение правой границы текста определяется несколько проще: его задают указанием длины строки. Производя операцию заполнения строки, программа nroff помещает в строке число символов, равное заданной длине строки. Задать длину строки можно с помощью директивы форматирования .11, например: .11 72 \" устанавливает длину строки равной 72 символам Вы заметили, как легко читать и понимать строки, содержащие директивы форматирования, если они к тому же содержат и комментарии? В свою очередь, длину страницы сформатированного текста можно задать с помощью директивы форматирования .pl, например: .pl 66 \" устанавливает длину страницы равной 66 строкам Рассмотрим теперь одну чрезвычайно важную директиву форматирования, управляющую размером страницы сформатированного текста — директиву форматирования .ро. С помощью этой директивы можно смещать всю стра- ницу сформатированного текста в обоих направлениях, сохраняя при этом заданное значение величины отступа. Это бывает очень полезно, если Вы хотите размещать каждую страницу сформатированного текста на листе бу- маги в специально отведенном ей месте. Например, при выводе сформати- рованного текста на фотонаборное устройство на одном листе бумаги, как правило, размещают сразу две страницы сформатированного текста (вывод в две колонки) — левую и правую; в этом случае левая граница текста пра- вой страницы сформатированного текста будет расположена справа от пра- вой границы текста левой страницы сформатированного текста. Такое рас- положение страниц сформатированного текста необходимо для дальнейшего переплета документа. Вряд ли машинистке поможет знание окончательного взаимного расположения на бумаге страниц документа. Вообразите себе ее проблемы в том случае, когда дополнительная правая страница возникает лишь в начале длинного документа. Все они могут быть автоматически раз- решены с помощью директивы форматирования .ро, например: .ро +10 \" смещает страницу на 10 позиций ВПРАВО .ро \" восстанавливает первоначальное положение страницы .ро — 10\" смещает страницу на 10 позиций ВЛЕВО Вы, наверное, заметили, что директива форматирования .ро может исполь- зоваться без параметров, как во второй строке примера. Директив формати- 152
рования, функционирующих подобным образом, вполне достаточно. Одни директивы форматирования, будучи применены без параметров, функциони- руют так, как если бы их параметры имели некоторое стандартное (принима- емое по умолчанию) значение, другие используют в этом случае параметры директивы форматирования с этим же именем, содержащиеся в предыдущей директиве форматирования с этим именем. Таким образом, значения пара- метров директив форматирования второго типа запоминаются программой nroff для дальнейшего использования. Обычно директивы форматирования сохраняют таким образом лишь одно значение параметра. Как и в большинстве случаев, самым надежным способом изучения дирек- тив форматирования является их экспериментальное использование. Большин- ство директив форматирования функционируют описанным выше образом. Однако мы советуем Вам как следует изучить Руководство, выяснить, какие директивы форматирования Вам доступны, и затем составить для себя не- большой отчет. 7.4. МАКРОДИРЕКТИВЫ ФОРМАТИРОВАНИЯ Познакомившись с некоторыми основными операциями форматирования, перейдем теперь к описанию более экзотических особенностей этого процес- са. Может быть, некоторые читатели после такого вступления отбросят нашу книгу, предоставив разбираться с ней тем, кто уже имеет большой опыт ис- пользования форматоров. Мы никогда не утверждали, что Вам удастся легко "приручить" программу nroff, поэтому не будет ничего удивительного в том, что, попав в плен Вашего внимания, программа nroff попытается "удрать" из него, продемонстрировав тем самым свои колоссальные возможности. При описании макродиректив форматирования мы обратим особое внима- ние на следующие два аспекта: написание макродиректив форматирования и использование их. Оба из них мы постараемся раскрыть достаточно полно. Даже если Вы не собираетесь создавать собственные макродирективы фор- матирования, используя уже существующие. Вы намного упростите процеду- ру форматирования текста. 7.4.1. ЧТО ТАКОЕ МАКРОДИРЕКТИВА ФОРМАТИРОВАНИЯ? Макродиректива форматирования — это набор директив форматирования или макродиректив форматирования, имеющий уникальное имя. Будучи оп- ределена однажды, макродиректива форматирования может быть применена таким же точно способом, как любая директива форматирования. Когда прог- рамма nroff анализирует очередную строку исходного текста и обнаруживает в первой ее позиции один из двух символов: . или ', она обращается к своей внутренней таблице имен. Дело в том, что при определении Вами макроди- рективы форматирования ее уникальное имя заносится именно в эту внут- реннюю таблицу программы nroff. Если строка, содержащая директиву фор- матирования в действительности содержит имя макродирективы, то програм- ма nroff заменяет эту строку исходного текста на последовательность строк. 153
содержащих директивы форматирований, соответствующие данной макроди- рективе форматирования. Эта особенность программа! nroff чрезвычайно важна, так как позволяет персонифицировать ее, т. е. за счет введения определений новых макродирек- тив "настроить" программу nroff на решение конкретных задач. Вы можете создать целый набор своих собственных макродиректив форматирования, удобных для решения Ваших задач; применение этих макродиректив вызы- вает введение в исходный текст документа соответствующих им наборов директив форматирования. 7.4.2, ПАКЕТЫ МАКРОДИРЕКТИВ ФОРМАТИРОВАНИЯ Доступные Вам базовые директивы форматирования должны быть доста- точно примитивны, чтобы не накладывать сколько-нибудь существенных ограничений на результат форматирования. Если перед Вами встанет задача быстрого и сложного форматирования достаточно большого документа, то очень скоро Вы убедитесь, что без помощи макродиректив форматирования здесь просто не обойтись, если, конечно, Вы не испытываете особого наслаж- дения, вводя с терминала раз за разом одну и ту же последовательность од- них и тех же команд. По этой причине в состав ОС UNIX введено несколько пакетов макроди- ректив форматирования (наборов хорошо проверенных и полезных макро- директив форматирования). К сожалению, единого, стандартного для всех версий ОС UNIX пакета макродиректив форматирования не существует, поэтому Вам придется обратиться за помощью к документации, описывающей пакеты макродиректив форматирования, имеющиеся в Вашей версии ОС UNIX. 7.4.3. ВКЛЮЧЕНИЕ ФАЙЛОВ Всякая макродиректива форматирования должна быть определена до ее использования. Обычной практикой является подготовка определений мак- родиректив форматирования в отдельном файле, содержимое которого за- тем включается в состав файла, содержащего форматируемый текст, с по- мощью директивы форматирования .so. Директива форматирования .so позволяет включить содержимое файла, имя которого является ее параметром, в состав входного файла программы nroff. Механизм ее функционирования чрезвычайно напоминает механизм функционирования оператора # include в языке программирования Си. На- пример, применение директивы форматирования .so macro__file приведет к включению файла macro_file в состав входного файла и поме- щению его вместо строки, содержащей вышеприведенную директиву формати- рования. Единственное функциональное отличие директивы форматирования .so от оператора #include в языке программирования Си состоит в том, что 154
имя файла, являющееся параметром директивы форматирования .so, не мо- жет быть заключено в двойные кавычки. Если Вы поместите вышеприведен- ную строку в самом начале подлежащего форматированию текста, то все макродирективы форматирования, определения которых имеются в файле macro_file, будут доступны Вам в любой момент. В качестве альтернативы можете воспользоваться готовыми пакетами макродиректив форматирования, для чего необходимо вызвать на выполне- ние программу nroff с флагом — mmacros. Следующая команда, например nroff - mmacros file вызывает включение содержимого файла /usr/lib/tmac.macros во входной файл программы nroff. 7.4.4. ПРИМЕРЫ ПРОСТЫХ МАКРОДИРЕКТИВ ФОРМАТИРОВАНИЯ Теперь, наконец, мы готовы определить несколько макродиректив форма- тирования. Рассмотрим процедуру форматирования начала нового параграфа. Последовательность директив форматирования, примененных для этого, уже рассматривалась нами, однако создание для этой цели макродирективы фор- матирования представляется нам более целесообразным. Определим макро- директиву форматирования начала нового параграфа следующим образом: . de pg \" определяет макродирективу форматирования с именем pg .br \" выводит остаток текста предыдущего параграфа .sp 2 \" пропускает 2 строки в сформатированном тексте .пе 3 \" резервирует 3 строки для текста начала параграфа . ti + 2 \" сдвигает первую строку на две позиции вправо \" конец определения макродирективы форматирования Тело макродирективы форматирования в приведенном примере включает все строки, содержащие директивы форматирования (при этом они могут содержать не только имя директивы форматирования) и расположенные между строками, содержащими соответственно имя директивы форматиро- вания .de и последовательность символом .. . Мы надеемся,что комментарии помогли Вам уяснить смысл приведенных в примере директив форматирова- ния, за исключением, быть может, директивы форматирования .пе. Эта ди- ректива сообщает программе nroff о необходимости зарезервировать на теку- щей странице по крайней мере три строки, если же это невозможно, то прог- рамма nroff автоматически переводит страницу, так как нежелательно размещать в самом конце текущей страницы сформатированного текста лишь одну или две строки начала нового параграфа. Использовать вышеописанную макродирективу форматирования можно, например,следующим образом: Здесь расположен текст первого параграфа. РЭ 155
А здесь расположен текст второго параграфа. И так далее для каждого нового параграфа. Мы привели здесь очень простой, но весьма наглядный пример использова- ния макродирективы форматирования для решения раз и навсегда наших проблем. Макродиректива форматирования начала параграфа должна быть тщательно проверена, после чего ее можно использовать где угодно. 7.4.5. МАКРОДИРЕКТИВЫ ФОРМАТИРОВАНИЯ С ПАРАМЕТРАМИ Теперь, когда макродиректива форматирования начала нового параграфа успешно функционирует^ Вас, вероятно, возникает желание создать макро- директиву форматирования заголовка раздела. Предположим, что тексты всех заголовков новых разделов должны быть подчеркнуты и, кроме того, каж- дый новый раздел будет начинаться с нового параграфа. Одним из возмож- ных определений необходимой нам макродирективы форматирования может быть следующее: .de se \" определяет макродирективу форматирования с именем se • br \" выводит остаток предыдущего текста .sp 2 \" пропускает 2 строки между разделами .пе 4 \" резервирует 4 строки • ul \" подчеркивает содержимое следующей введенной строки \ \$1 \" помещает на место этой строки первый параметр определяе- \" мой макродирективы форматирования (смотри ниже) pg \" форматирует начало нового параграфа \" конец определения макродирективы форматирования Рассмотрим пример использования только что определенной нами макроди- рективы форматирования заголовка раздела: .se ''Раздел номер 1" Это первый параграф в этом новом разделе. Желательно, чтобы он был коротким, тогда останется место для других примеров. .se "Раздел номер 2" Текст раздела 2. Заметим, что в приведенном выше примере использования макродирек- тивы форматирования .se строка текста, являющаяся ее параметром, заклю- чена в двойные кавычки. Это сделано во избежание интерпретации символов ш, разделяющих помещенные в строку слова, в качестве разделителей парамет- ров макродирективы форматирования .se. В противном случае каждое из трех слов: "Раздел", "номер", "1" — будет использовано макродирективой форматирования .se как отдельный параметр, ибо стандартным разделителем параметров макродиректив форматирования является символ ш. 156
Попытаемся теперь объяснить введенное нами определение макродиректи- вы форматирования .se. Все в нем достаточно просто и понятно, за исключе- нием, быть может,таинственной строки \\$1 которая, кажется, содержит подчеркнутый текст. Присутствие такой строки специального вида указывает программе nroff на то, что первый параметр макродирективы форматирования .se должен быть помещен на странице сформатированного текста на месте этой строки. А зачем здесь столько сим- волов \? 7.4.6. СИМВОЛЫ, ИМЕЮЩИЕ ДЛЯ ПРОГРАММЫ nroff СПЕЦИАЛЬНЫЙ СМЫСЛ Так же как внутри определения макродиректив форматирования, на любом этапе процесса форматирования текста Вам может понадобиться использова- ние символов, имеющих для программы nroff специальный смысл. Одним из таких символов является символ \. Этот символ указывает программе nroff на необходимость специального использования символов, следующих непо- средственно за ним. Символ, воспринимаемый некоторой программой подоб- ным образом, называют обычно управляющим символом этой программы. Выбор в качестве управляющего символа программы nroff символа \ не слишком удачен, ибо и редактор текстов ed, и драйвер терминала (драйвер терминала — это компонента ОС UNIX, управляющая работой Вашего терми- нала) воспринимают символ \ аналогичным образом. А это означает, что по- местить в текстовом файле собственно символ \ довольно сложно, в отли- чие от подавляющего большинства прочих символов. К счастью, собственно символ \ редко используется вне определений макродиректив форматирова- ния; поэтому регулярного преодоления сложностей ввода этого символа чаще всего удается избежать. Чтобы познакомиться с полным списком символов, имеющих для прог- раммы nroff специальный смысл. Вам придется обратиться к документации, описывающей программу nroff, имеющуюся в используемой Вами версии ОС UNIX. Особый интерес для Вас должно представлять использование сим- вола $ в качестве указателя параметра макродирективы форматирования. Непосредственно за символом $ в теле макродирективы форматирования должна следовать цифра, указывающая программе nroff на то, какой из пози- ционных параметров макродирективы форматирования должен быть разме- щен на месте строк и, содержащей рассматриваемый символ $. Таким способом можно передать макродирективе форматирования до девяти параметров. Как мы уже говорили, все символы, имеющие для программы nroff спе- циальный смысл, должны предваряться символом \. Теперь Вам должен быть понятен смысл конструкции \ $1, однако в рассмотренном выше примере символу $ предшествуют два символа \. Почему? Второй символ \ предотвращает интерпретацию программой nroff первого символа \. Поясним сказанное. Всякая макродиректива форматирования об- 157
рабатывается программой nroff дважды: первый раз при ее определении и второй раз при выполнении. На этапе определения макродирективы форма- тирования программа nroff интерпретирует все символы \ и тем самым — ин- терпретирует последовательность символов \$1 описанным выше образом. Однако на этом этапе первого параметра макродирективы форматирования просто не существует, поскольку мы лишь определяем ее. Следовательно, интерпретация последовательности символов \$1 приведет к подстановке на место содержащей ее строки пустой строки символов. Таким образом, в теле определенной нами макродирективы форматирования окажется пустая строка и все наши планы рухнут. Если же мы воспользуемся последователь- ностью символов \\S1, то в результате интерпретации программой nroff этой последовательности символов на этапе определения макродирективы форматирования останется всего один символ \ и, следовательно, на этапе выполнения макродирективы форматирования в ее теле окажется строка вида \$1 Тогда если определенная нами макродиректива форматирования применена с параметром, то он успешно займет предназначенное ему место в сформати- рованном тексте. Чрезвычайно важно понять разницу между этапом определения макроди- рективы форматирования, когда программа nroff обрабатывает строки, со- держащие директивы форматирования, находящиеся в теле определяемой макродирективы форматирования и сохраняет их для дальнейшего исполь- зования, и этапом выполнения макродирективы форматирования, когда результаты этапа определения макродирективы форматирования вставля- ются в форматируемый текст и заново обрабатываются программой nroff, что и приводит к передаче определенной на первом этапе макродирективе форматирования ее параметров. 7.5. ОФОРМЛЕНИЕ СТРАНИЦ В отличие от многих других форматоров программа nroff не представля- ет пользователям каких-либо стандартных средств автоматического оформ- ления страниц сформатированного текста. Возможно, что теперь Вам станет очевидна необходимость применения некоторых макродиректив формати- рования, хотя бы для решения этой задачи. Все макродирективы форматирования могут использоваться двумя спо- собами; уже описанным способом включения имен макродиректив формати- рования в текст, подлежащий форматированию, или же выполнением макро- директив форматирования в тот момент, когда порядковый номер текущей форматируемой строки достигает определенного значения. Такой меха- низм вызова макродиректив форматирования называют обычно вызовом по строке. 158
7.5,1. МАКРОДИРЕКТИВЫ ФОРМАТИРОВАНИЯ ДЛЯ ОФОРМЛЕНИЯ СТРАНИЦ Введем две традиционные макродирективы форматирования для оформле- ния страниц сформатированного текста. Одна из них автоматически выполня- ется в начале каждой новой страницы, а вторая — в конце. Двух приводимых ниже примеров вполне достаточно для успешного использования обеих мак- родиректив форматирования. Заметим только, что возможности механизма вызова по строке выходят далеко за рамки описания, предложенного нами в этой книге. Для более детального изучения макродиректив оформления стра- ниц обратитесь к [9]. Рассмотрим следующие директивы форматирования: ,wh 0 Th wh - 3 Tf Первая из них в качестве параметра содержит имя макродирективы фор- матирования .Th и указывает на необходимость выполнения макродиректи- вы форматирования .Th перед началом форматирования первой строки каж- дой новой страницы форматируемого текста. Вторая директива форматиро- вания в качестве параметра содержит имя макродирективы форматирования .Tf иуказываетнанеобходимость выполнения макродирективы форматиро- вания .Tf перед началом форматирования третьей от конца строки каждой новой страницы форматируемого текста. Общий формат директивы форматирования .wh следующий: .wh N хх Это означает, что макродиректива форматирования хх должна быть выпол- нена перед форматированием N-й строки каждой новой-страницы формати- руемого текста, при этом символ — указывает на то, что N-ю строку необхо- димо отсчитывать от конца страницы. В заключение отметим, что программа nroff не предоставляет пользовате- лям возможности передавать параметры макродирективе форматирования, вызываемой по с!роке. 7.5.2, ПРИМЕРЫ МАКРОДИРЕКТИВ ФОРМАТИРОВАНИЯ ДЛЯ ОФОРМЛЕНИЯ СТРА- НИЦ Если Вы задались целью создать пакет стандартных макродиректив фор- матирования для оформления страниц форматируемого текста, то не забудьте о пользователе и предоставьте ему хотя бы минимальную свободу в выборе формата и текстов оформления. Приведем теперь пару примеров, навеянных этим рассуждением: .de Th \" определяет макродирективу форматирования заголовка страницы с именем Th 'ti ' Левый Заголовок' Центральный Заголовок' Правый Заголо- вок' 'sp 2 \" пропускает 2 строки в сформатированном тексте 'ns \" возврат к режиму без пропусков конец определения макродирективы форматирования 15Э
Директива форматирования 'tl выводит на страницу сформатированного текста заголовок, состоящий как это показано в примере, из трех частей. Любая часть заголовка может быть опущена, в этом случае она просто не бу- дет выведена на сформатированную страницу. Например, следующая дирек- тива форматирования: 'tl "'1-е Января, 1970' выводит в качестве заголовка сформатированной страницы указанную дату, помещая ее в правом верхнем углу страницы. Вы, наверное, заметили, что все строки, содержащие директивы формати- рования в теле макродирективы форматирования, определенной в примере, начинаются с символа ', запрещающего разрыв формата, вызываемый в обыч- ной ситуации директивами форматирования, имена которых содержатся в указанных строках. В данном случае это необходимо, ибо в противном слу- чае будут немедленно выведены все незаполненные строки предыдущей стра- ницы и вслед за ними — две пустые строки, а это не совсем то, что нам нуж- но. При написании макродиректив форматирования, подобных описанной в этом примере, надо быть очень осторожным и тщательно проверять их в ра- боте, во избежание самых неожиданных эффектов. Мы советуем Вам начинать работу с запрещения разрыва формата и не разрешать его до тех пор, пока не убедитесь, что это Вам действительно необходимо. Директива форматирова- ния 'ns указывает программе nroff на необходимость возврата к режиму без пропусков. Это означает, что программа nroff игнорирует любую директиву форматирования, вроде .sp или .Ьр, обрабатываемую этой программой после директивы форматирования 'ns. Режим без пропусков автоматически отклю- чается после вывода первой же строки сформатированного текста. И еще один пример — определение макродирективы форматирования для оформления страницы: .deTf \" определяет макродирективу форматирования колонцифр страниц с именем Tf 'sp 1 \" пропускает 1 строку в сформатированном тексте 'tl "Страница %" 'Ьр \" начинает страницу без разрыва формата \" конец определения макродирективы форматирования Выведенная колонцифра будет включать текст "Страница" с последующим номером страницы, расположенный в середине строки. Символ %, использо- ванный в определении макродирективы форматирования, во время ее вы- полнения автоматически заменяется на порядковый номер форматируемой страницы. И вновь все строки, содержащие директивы форматирования, начинают- ся с символа '. Строка, содержащая директиву форматирования 'Ьр, вызыва- ет перевод страницы, но разрыва формата вызывать не должна. 160
7-6. ДОПОЛНИТЕЛЬНЫЕ ОСОБЕННОСТИ Этот параграф, конечно, не для робких новичков. Скорее наоборот. Его цель — рассказать потенциальным пользователям программы nroff о допол- нительных интересных возможностях этой программы. Если высказанное предостережение не остановит Вас, то это означает, что Вы готовы к посвя- щению в укротители программы nroff. 7.6.1. ВНУТРЕННИЕ РЕГИСТРЫ ПРОГРАММЫ nroff Внутренние регистры — это основные переменные программы nroff, кото- рые могут принимать любые числовые значения и использоваться везде, где может использоваться любое число. Например, следующие директивы форма- тирования: .пг а 1 .nr аа 2 объявляют два внутренних регистра. Внутренний регистр а инициализирован числом 1, а внутренний регистр аа инициализирован числом 2. Значения объяв- ленных регистров могут быть использованы в директивах форматирования следующего, например, вида: .sp \па .пе \п(аа Первая из приведенных в примере директив форматирования означает про- пуск одной позиции, а вторая — резервирование двух позиций. Помещение имени внутреннего регистра вслед за последовательностью символов \п нуж- но для указания программе nroff на необходимость использования значения указанного внутреннего регистра. Если имя внутреннего регистра состоит из двух символов, то во избежание двусмысленного толкования программой nroffтакого имени перед ним должен быть помещен символ ( . Например, следующий фрагмент текста: теперь это \n (ааат, а было \naam. в результате форматирования приобретет следующий вид: теперь это 2am, а было 1am. Значение внутреннего регистра может автоматически увеличиваться или уменьшаться на заданную величину. Это бывает необходимо, например, для автоматической нумерации глав и параграфов. Указать необходимость авто- матического увеличения значения внутреннего регистра можно с помощью символа +, помещенного непосредственно перед именем внутреннего регист- ра, например: .пг х 2 М внутренний регистр х имеет значение \п+х 6 Зак 1165 161
Первая из приведенных строк является директивой форматирования, в кото- рой объявляется внутренний регистр х и содержатся указания на необходи- мость инициализации внутреннего регистра х числом 2, а также на необходи- мость автоматического увеличения его значения на число М каждый раз, когда в строке форматируемого текста перед именем этого внутреннего ре- гистра стоит символ + (как, например,это имеет место во второй строке при- мера) . Если М — отрицательное число, то в результате вместо автоматичес- кого увеличения программа nroff осуществит автоматическое уменьшение значения внутреннего регистра х. Чаще всего число М — это просто 1, однако этот факт не дает нам права опустить число М в директиве форматирования ,пг. Вы должны хорошо запомнить, что при автоматическом увеличении значения внутреннего регистра его значение будет сначала увеличено и только затем использовано программой nroff. Если в приведенном выше примере М = 1, то в результате форматирования будет получен следующий текст: внутренний регистр х имеет значение 3 Как и ранее, продлить жизнь символа \ в определении макродиректив форматирования можно, лишь поместив перед ним еще один символ \, поэтому при определении макродиректив форматирования вместо последовательности символов \п необходимо использовать последовательность символов \\п. Существует целый ряд внутренних регистров, используемых самой прог- раммой nroff и доступных в той или иной мере пользователю. Некоторые из них доступны лишь для чтения, другие могут быть модифицированы поль- зователем, однако неосторожная модификация некоторых из них может при- вести к непредсказуемым результатам. Получить совершенно достоверную информацию по этому вопросу можно только в [ 9]. 7.6.2. СТРОКОВЫЕ ПЕРЕМЕННЫЕ Существует несколько способов объявления и инициализации строковых переменных. В основном все эти операции задаются одной строкой, содержа- щей директиву форматирования. Например, следующие директивы формати- рования: .пг х 2 .ds S Это строка .ds S2 Строка \пх объявляют внутренний регистр х и строковые переменные S и S2, а также указывают на необходимость инициализации их некоторыми значениями. Значения объявленных переменных могут быть использованы в тексте. На- пример: В строке S стояло: \*S В строке S2 стояло:\*(S2 В результате форматирования этот фрагмент текста примет следующий вид: В строке S стояло: Это строка В строке S2 стояло: Строка 2 162
Здесь, как и для случая внутренних регистров, перед именами строковых пе- ременных, состоящими из двух символов, при появлении их в форматируе- мом тексте должен находиться символ (. Строки, содержащие директивы форматирования, могут содержать произвольные комбинации имен внутрен- них регистров и имен строковых переменных. И, наконец, как и для случая внутренних регистров, не забывайте о необходимости использования соответ- ствующего числа символов \. 7.6.3. ПЕРЕДАЧА Как мы уже говорили, на этапе определения макродиректив форматирова- ния программа nroff обрабатывает строки тел макродиректив форматиро- вания и сохраняет их для дальнейшего использования. Из этого можно сде- лать один важный вывод: всякую определенную макродирективу формати- рования можно интерпретировать как некую именованную область и, следо- вательно, можно использовать как буфер. Строки текста независимо от вида их содержимого можно скопировать из одного такого буфера в другой с по- мощью механизма, называемого обычно передачей: сформатированный текст вместо обычного вывода на стандартный вывод может быть передан в соз- данный заранее описанным выше способом буфер. Такая процедура позволит Вам модифицировать каким-либо способом уже сформатированный текст, например ввести в него подписи к рисункам и схемам, если таковые име- ются -в Вашем тексте. Директивы форматирования, содержащие указание на необходимость передачи, могут иметь один из двух следующих форматов: .di хх \" передать макродирективе форматирования хх; прежнее CO- X'' держимое хх потеряно .da хх \" передать макродирективе форматирования хх, добавляя к ее \" прежнему содержимому В обоих случаях отсутствие параметров у директив форматирования .di и .da приведет к запрещению передачи. Некоторые версии программы nroff до- пускают использование вложенных конструкций передачи, однако в обыч- ной практике необходимость в этом возникает крайне редко, а потому мы не станем ее обсуждать в рамках этой книги. Итак, после того как мы задали режим передачи, весь исходный текст, вводимый программой nroff из входного файла, после форматирований будет выведен в некоторый созданный для этого буфер, в нашем случае буфер хх. Передача будет прекращена в тот момент, когда очередная форма- тируемая строка окажется строкой, содержащей директиву форматирова- ния .da или директиву форматирования .di без параметров. Разница между двумя вариантами указания режима передачи заключает- ся в том, что директива форматирования .di уничтожает прежнее содержи- мое буфера, а директива форматирования .da добавляет передаваемый текст к прежнему содержимому буфера. 6; 163
Текст, переданный в буфер, может храниться там неограниченно долго. Число строк переданного текста (при этом строки, содержащие директивы форматирования не подсчитываются) присваивается внутреннему регистру dn для дальнейшего использования; понятно, что каждая новая передача изменяет значение этого внутреннего регистра. Чтобы вывести текст, помещенный в буфер, воспользуйтесь именем буфе- ра так, как если бы это было имя обычной макродирективы форматирования, например: .XX выводит содержимое буфера хх Если Вы помните, применение макродиректив форматирования вызывает подстановку в форматируемый текст набора директив форматирования, со- ответствующего примененной макродирективе форматирования. А раз так, то применение в качестве макродирективы форматирования буфера приве- дет, очевидно, к подстановке в форматируемый текст его содержимого. Очень часто бывает необходимо удалить ту или иную макродирективу форматирования, если она нам больше не понадобится, или тот ипи иной бу- фер, если его содержимое уже выведено. В обоих случаях эта процедура ос- вобождает место во внутренней таблице имен программы nroff, например: .гт хх \* удаляет макродирективу форматирования хх 7.6.3.1. Передача форматируемого текста Чрезвычайно важно уяснить, что при передаче текст подвергается частич- ному форматированию. Это означает, что все управляющие символы обрабаты- ваются программой nroff. Если передаваемый текст содержит директивы фор- матирования, то возникает необходимость позаботиться о необходимом коли- честве символов \в их составе, так как включение этих строк в формутиру- емый текст произойдет лишь после форматирования программой nroff строки, содержащей имя буфера, в который была произведена указанная передача. От момента заполнения буфера и до момента окончательного вывода его содер- жимого значения некоторых основных переменных программы nroff, имена которых включены в текст, переданный в буфер, могут быть изменены Вами, ^поэтому во избежание неприятных сюрпризов внимательно следите за моди- фикацией значений таких переменных. Вообще, советуем Вам, особенно если речь идет о полных строках текста, избавляться от буферов сразу по завершении их использования. Рекомендуем использовать следующую, на наш взгляд очень удобную, последовательность директив форматирования: .ео \" запрещает обработку управляющих символов nf \” запрещает заполнение строк •хх \" выводит содержимое буфера хх .ес разрешает обработку управляющих символов гт хх \" удаляет буфер хх 164
Если переданный в буфер текст содержит имена внутренних регистров или строковых переменных, необходимых Вам для дальнейшего использования, то запрещать обработку управляющих символов нельзя1. 7.6.32. Прозрачные строки Удобным средством сохранения управляющих символов при передаче текста является механизм прозрачных строк. Если строка текста начинается с последовательности символов \!, то при передаче ее в буфер ни один из со- держащихся в ней управляющих символов не будет обработан программой nroff. Следующий фрагмент текста при форматировании вызывает передачу текста в буфер хх: .di хх \| НАЧАЛ О \!.Ьг -\l.ul \!Строка, \\*(ху \!.Ьг \!лараметром, \\$1 .di Сформатируем приведенный фрагмент текста с помощью двух простых мак- родиректив форматирования: .ds ху завершенная .хх "подтверждающая, что все работает" В результате мы должны получить текст следующего вида: НАЧАЛО Строка, завершенная параметром, подтверждающая, что все работает 7.6.4. УСЛОВНЫЙ ВВОД При использовании макродиректив форматирования иногда бывает необ- ходимо указать, какие из строк, содержащих директивы форматирования, помещенные в ее теле, должны обрабатываться программой nroff и в каком порядке. Такая возможность существует, она реализуется с помощью дирек- тивы форматирования .if, имеющей следующий формат: .if условие строка текста или команды 1 Все, содержащиеся в передаваемом тексте имена внутренних регистров или строко- вых переменных, заменяются программой nroff.при передаче на соответствующие им значения. (Прим, ред.) 165
При обработке такой строки программа nroff производит проверку условия, и в этом случае, если условие удовлетворяется (получено ненулевое значение), часть строки, следующая непосредственно за условием, вводится на место всей строки, в противном случае часть строки, следующая непосредственно за условием, игнорируется. Чаще всего условия представляют собой арифме- тические выражения, содержащие имена внутренних регистров или строко- вых переменных. Сложность подобных выражений может быть произвольной. В качестве примера приведем определение простой макродирективы форма- тирования, применение которой позволяет выводить заданный текст в ка- честве заголовка на всех страницах сформатированного текста, кроме первой: .de Th \" определяет макродирективы форматирования заголовка с именем Th .if \\ п % —1 'tI "Страница %" .if \\n %-1 'sp 2 'ns \" конец определения макродирективы форматирования Очевидно, что описанное в примере условие будет выполнено лишь тогда, когда номер форматируемой страницы текста превысит 1. Вы, по-видимому, обратили внимание на то, что строка, содержащая директиву форматирова- ния .if, начинается с символа .. Это связано с тем, что использование дирек- тивы форматирования .if, созданной специально для включения в макроди- рективы форматирования, не вызывает разрыва формата. В качестве следую- щего примера рассмотрим небольшую модификацию описанной ранее макро- директивы форматирования .se, в результате которой упомянутая макроди- ректива форматирования будет вести себя немного разумнее при использова- нии ее без параметров: .de se .br .sp 2 .ne 4 .if \\w'\\$1' .ul \" проверяет наличие параметров и .if \\w’\\$1' \\$1 \" подчеркивает их, если они присутствуют pg Строки, содержащие директивы форматирования .if, специально выделены с помощью комментариев. Первое из условий в приведенном выше примере содержит обращение к функции width — встроенной функции программы nroff. На обращение к этой функции указывает последовательность символов \w. Если макродиректива форматирования .se применена с параметром, то функция width вернет в качестве своего значения длину строки, являющейся параметром макродирективы форматирования .se, т. е. положительное число, а в противном случае будет возвращен 0. Итак, если макродиректива формати- рования .se применена с параметром, то в результате форматирования он будет 166
выведен и подчеркнут, если же макродиректива форматирования .se примене- на без параметра, то не будет сделано ничего. Предыдущая версия макродирективы фоматирования ,se, примененная без параметра, вызвала бы подчеркивание первой строки следующего пара- графа. Разобрались почему? Результат проверки можно инвертировать, помещая непосредственно перед условием символ !. Например, строка .if । \w'\\$1' Нет параметра помещенная в произвольном месте тела макродирективы форматирования .se приведет к выводу после форматирования сообщения об отсутствии заголов- ка. О директиве форматирования .if можно говорить еще очень много, но мы не станем этого делать, втайне надеясь, что Вы изучите ее подробнее, обра- тившись к [ 9]. 7.7. РЕКОМЕНДАЦИИ ПО НАПИСАНИЮ МАКРОДИРЕКТИВ ФОРМАТИРОВАНИЯ Этот параграф предназначен как для новичков, захотевших поэксперимен- тировать с программой nroff, так и для ''nroff-маньяков", вознамеривших- ся создать пакет макродиректив форматирования на любой вкус. Небольшое предостережение: Если Вы относитесь ко второй категории, то знайте, что всегда найдется пользователь, который спросит Вас сквозь слезы: "А почему он не делает этого?" Вы, конечно, можете ответить, что не думали об этом, но мы-то с Вами знаем, что невозможно заранее рассчитать, сколько символов \ понадобиться, чтобы справиться с задачей этого пользователя. В конце кон- цов Вам придется взять листинг спорной компоненты Вашего пакета и пере- делать ее, чтобы пользователь, наконец, успокоился. Первое золотое правило при написании макродиректив форматирования, как,- впрочем, и любой программы, — это использование комментариев. Существует очень немного хорошо комментированных пакетов мак- родиректив форматирования, соответственно очень немногие из существу- ющих пакетов макродиректив форматирования можно понять. Не бойтесь подробно комментировать свои действия, даже если это займет целый день. Второе правило: при наименовании внутренних регистров и строковых переменных пользуйтесь.осмысленными именами, хотя бы и из двух симво- лов. Некоторые из этих имен будут неизбежно недостаточно мнемоничными, поэтому в любом случае не забывайте комментировать их. Не забудьте, что имена переменных в Вашем пакете и имена переменных, использованные в исходном тексте, так сказать глобальные имена,должны различаться. Избегайте использовать встроенные константы. Если Вам нужно исполь- зовать константу, отведите для нее внутренний регистр с подходящим име- нем, соберите все необходимые Вам константы в созданном специально для этого файле и разрешите пользователям доступ к этому файлу. Маловероят- но, чтобы все пользователи полностью приняли Вашу манеру форматирования 167
текстов, но многие из них с радостью воспользуются Вашим пакетом макро- директив форматирования. Файл, содержащий константы, должен вводиться в текст документа в первой его строке со всем пакетом макродиректив фор- матирования. Рассмотрим пример определения макродиректив форматирования: : "Файл констант .nr PI 2 \" отсуп для первой строки параграфа .nr PS 2 \" расстояние между параграфами .nr PN 3 \" резервирование для нового параграфа \" макрокоманда параграф •de pg .br sp \\n(PS .ne \\n(PN .ti +\\n(PI \" пропускает PS строк \" резервирует PN строк на этой странице \" временный отступ на PI позиций Как Вы уже заметили, приведенная в этом примере макродиректива фор- матирования вызывает оформление нового параграфа. Заглавные буквы в именах введены для различения имен команд форматирования и имен пере- менных программы nroff, объявленных Вами, а также для повышения удобо- читаемости этой макродирективы форматирования. При определении макродирективы форматирования нового параграфа желательно запретить интерпретацию внутренних регистров на этапе опреде- ления. Если удалить один из символов \ , помещенных перед именем любого внутреннего регистра в теле макродирективы форматирования, то примени- мость макродирективы форматирования резко упадет, ибо на этапе ее испол- нения на месте каждого объявленного в ней внутреннего регистра будет сто- ять значение, присвоенное ему еще на этапе определения этой макродиректи- вы форматирования. А это означает, что все параграфы будут сформатированы одинаковым образом или же Вам придется переписывать эту макродирективу форматирования. Запрещая интерпретацию внутренних регистров на этапе определения макродиректив форматирования. Вы сохраняете их в виде именнованных переменных и всегда можете изменить их значения. Для проверки созданных Вами макродиректив форматирования мы мо- жем посоветовать воспользоваться совершенно великолепной, но, к сожале- нию, малопопулярной директивой форматирования .tm, которая предназначе- на для вывода на терминал сообщений в процессе форматирования. Например, следующая директива форматирования: .tm нечто, включающее строки приводит к выводу на стандартный протокол (в общем случае это терминал) всех своих параметров после обработки программой nroff всех внутренних регистров и строковых переменных, имена которых присутствуют в форма- 168
тируемом тексте. Применение директивы форматирования .tm — это единст- венный способ точно узнать где и когда действительно обрабатывается прог- раммой nroff та или иная макродиректива форматирования. Других средств отладки программа nroff не имеет. Еще раз напоминаем Вам, что для того чтобы предотвратить интерпрета- цию программой nroff управляющего символа \ на этапе определения макро- директив форматирования, необходимо перед ним поместить еще один сим- вол \. 7.8. ЗАКЛЮЧЕНИЕ Глава, которую Вы прочли, вряд ли сделает из Вас специалиста по исполь- зованию программы nroff,однако содержащейся в ней информации достаточ- но для того, чтобы относительно легко прочесть различные руководства, опи- сывающие программу nroff, а также самому написать несколько простых макродиректив форматирования. А вдруг после такой подготовки в один прекрасный день Вы все-таки сумеете подготовить некоторый документ с помощью программы nroff. Кто знает? ГЛАВА 8. ПРОЦЕССЫ И СИСТЕМНЫЕ ВЫЗОВЫ 8.1. ВВЕДЕНИЕ До сих пор, бережно держа Вас за руку, мы провели Вас через первые семь глав этой книги, в которых мы постарались осветить некоторые аспекты использования ОС UNIX. Как заботливая наседка кудахчет, созывая своих цыплят к найденному ею лакомому кусочку, так же и мы постарались пре- поднести Вам наиболее интересные аспекты ОС UNIX. Мы постарались создать Вам хорошую основу для восприятия более трудного материала, представлен- ного в последних главах книги. Некоторые пользователи не захотят продолжать чтение оставшихся трех глав книги — действительно, ведь можно использовать ОС UNIX не понимая ее внутреннего устройства и того, как она функционирует, поскольку для того, чтобы управлять автомобилем, вовсе не обязательно знать, как устроены двигатель внутреннего сгорания и коробка передач. Но мы надеемся, что Вы не относитесь к этой категории пользователей. В этой главе мы постараемся рассказать о некоторых системных вызовах. Как, Вы, наверное, помните, системные вызовы представляют собой интер- фейс между программами пользователей и ОС UNIX. Команды ОС UNIX про- изводят требуемые действия, используя для этого системные вызовы. Если бы системные вызовы отсутствовали, то программы могли бы обрабатывать только внутренние, т. е. имеющиеся внутри них данные. Все действия, связан- ные с вводом-выводом и вызовом программ на выполнение, реализуются с по- мощью системных вызовов. Системные вызовы отрабатываются той частью ОС UNIX, которая называется ядром ОС UNIX, поэтому правильнее было бы 169
сказать, что системные вызовы представляют собой интерфейс между прог- раммами пользователей и ядром ОС UNIX. Ядро ОС UNIX обеспечивает раз- деление единственного центрального процессора между выполняющимися параллельно программами (процессами), осуществляет управление памятью и управление устройствами, реализуют файловую систему. В качестве примера рассмотрим команду rm, обеспечивающую удаление файлов. При вызове на выполнение команды пл на выполнение вызывается программа, хранящаяся в файле /bin/rm, которой передаются аргументы, указывающие, какие файлы подлежат удалению. Вызванная на выполнение программа для каждого из указанных файлов проверяет, является ли поль- зователь, вызвавший ее на выполнение, владельцем указанных файлов и имеет ли он право доступа к ним по записи, а затем, если оба эти условия выполнены, используя системный вызов unlink, удаляет каждый из указан- ных файлов. Системный вызов unlink не производит таких проверок. Поэтому для того, чтобы исключить случайные ошибки, такая проверка была включе- на в команду пп. Рассмотрением этого примера мы попытались подчеркнуть разницу между командами ОС UNIX, использующими системные вызовы, и собственно системными вызовами. Мы надеемся, что Вы уловили разницу между ними. Что происходит, когда Вы вызываете на выполнение команду 1$ или сс, либо программу, хранящуюся в файле a.out? Ответ на этот вопрос мы по- пытаемся дать в этой главе. 8.2. ВЫЗОВ ПРОГРАММ НА ВЫПОЛНЕНИЕ Первый шаг, который мы сделаем, связан с объяснением того, что проис- ходит, когда Вы вызываете на выполнение какую-либо команду, используя для этого интерпретатор команд shell. Как Вы, наверное, помните, вызов на выполнение интерпретатора команд shell осуществляется программой login. Интерпретатор команд shell обеспечивает ввод команд с терминала и их выполнение. Некоторые команды, введенные с терминала, интерпретатор команд shell выполняет сам, но таких команд очень мало. Как правило, вводимые с терминала команды не имеют никакого отношения к самому интерпретатору команд shell. Введя с терминала команду такого типа, интерпретатор команд shell вызывает на выполнение программу, обеспечи- вающую выполнение требуемых действий, и передает этой программе указан- ные в командной строке аргументы. После этого интерпретатор команд shell переходит к ожиданию завершения выполнения этой программы, если только команда не была вызвана на выполнение в асинхронном режиме. Как мы уже говорили, интерпретатор команд shell является самой обыч- ной программой, не имеющей никаких особых привилегий; поэтому любые действия, выполняемые интерпретатором команд shell могут быть выполне- ны программой, написанной Вами. Отлично! Следовательно, любая прог- рамма с помощью системных вызовов может воспользоваться функциями, реализуемыми ядром ОС UNIX. Это означает, что все без исключения прог- 170
раммы в ОС UNIX могут использовать системные вызовы. Привилегии вы полняющейся программы определяются, как правило, привилегиями поль- зователя, вызвавшего эту программу на выполнение. Иногда привилегии выполняющейся программы определяются привилегиями владельца файла, в котором хранится эта программа, но здесь мы должны остановиться, а то мы уже слишком сильно забежали вперед. Давайте внимательно проследим за выполнением какой-нибудь простой команды. В качестве примера воспользуемся командой echo. Вспомните при- водившийся ранее пример: $ echo hello mike hello mi ke $ Что же происходит в результате ввода с терминала командной строки? Интерпретатор команд shell осуществляет ввод с терминала командной строки и выясняет, что в командной строке содержится одна-единственная команда, имеющая два аргумента. Затем интерпретатор команд shell произ- водит совершенно поразительное действие: он раздваивает сам себя на две копии, используя для этой цели системный вызов fork. В каждой из этих двух копий открыты одни и те же файлы и имеются совершенно одинаковый код и внутренние данные. Единственное отличие этих двух копий состоит в том, что каждая из них может определить, кем она является: процессом-предком или процессом-потомком. Здесь мы вынуждены прервать дальнейшее изложение материала с тем, чтобы ввести некоторые новые термины, о которых мы тблько что упомяну- ли. В соответствии с принятой в ОС UNIX терминологией мы будем называть процессом любую программу, находящуюся в состоянии выполнения. Вы должны понимать, что процесс — это нечто большее, чем просто программа. Сейчас мы попытаемся объяснить, в чем тут дело. Программа представляет собой "мертвый'' статический объект, не обладающий никакими возможнос- тями по совершению действий. В противоположность этому процесс представ- ляет собой "живой" динамический объект, "жизнь" которого подчиняется правилам, устанавливаемым программой. С определенной натяжкой прог- рамму можно уподобить ДНК, а процесс — живой клетке, управляемой этой ДНК. Процесс динамичен, он изменяется с течением времени и,быть может, в результате каких-либо внешних воздействий, а программа — это нечто застывшее и неизменное. Процесс, выполняющийся под управлением ОС UNIX, может сменить программу, определяющую его поведение. Это производится за счет отказа от текущей'программы, вызова новой программы и передачи ей управления. Теперь, введя понятие процесса, мы можем вернуться к обсуждению пове- дения двух копий процесса, в рамках каждой из которых выполняется ин- терпретатор команд shell. Процесс-предок немедленно теряет интерес к жиз- ни и переходит в состояние ожидания каких-либо событий в жизни процесса- потомка. Для того чтобы сообщить ядру ОС UNIX о переходе в состояние 171
ожидания, процесс-предок использует системный вызов wait. Процесс-пото- мок, в свою очередь, имеет намерение сменить программу, определяющую его поведение, и передать в новую программу список аргументов, необходи- мых для ее выполнения. Для того чтобы сообщить ядру ОС UNIX о своем на- мерении сменить программу, определяющую его поведение, процесс-потомок использует системный вызов ехес. В результате выполнения системного вы- зова ехес ядре] ОС UNIX освобождает оперативную память, занимаемую про- граммой, определяющей в данный момент поведение процесса, вызывает в оперативную память новую программу и передает ей управление и список ар- гументов. Таким образом, единственное, что изменилось в процессе-потомке, так это программа, определяющая его поведение, а все остальное осталось без изменения, в частности остались открытыми все файлы, которые были открыты до смены программы. Как Вы, наверное, уже догадались новой про- граммой, определяющей поведение процесса-потомка, является программа echo, которая выводит на стандартный вывод свои аргументы. Поскольку не- посредственно перед сменой программы в процессе-потомке файл, соответ- ствующий стандартному выводу, был открыт и назначен на терминал, то и программа echo выведет свои аргументы на терминал. Из сказанного должно быть понятно, почему в ОС UNIX так легко осущест- влять переназначение стандартного ввода-вывода. Например, если Вы для то- го, чтобы переназначить стандартный вывод команды echo в файл fred, вве- дите с терминала команду echo hello mike >fred то интерпретатор команд shell перед тем, как вызвать с помощью системного вызова ехес программу echo, просто переназначит свой собственный стандарт- ный вывод в файл fred. Программа echo даже не узнает о том, куда назначен ее стандартный вывод. Произведя все необходимые действия, предписываемые программой echo, процесс-потомок завершает свое существование, сообщив об этом ядру ОС UNIX с помощью системного вызова exit. Системный вызов exit является общепринятой в ОС UNIX формой "самоубийства". Выполнение системного вызова exit приводит к закрытию всех файлов, которые были открыты в процессе, издавшем этот системный вызов, и перемещению самого процесса в страну забвения в состоянии, называемом в ОС UNIX зомби1, до тех пор, пока непосредственный процесс-предок этого процесса не позаботится о нем. При выполнении системного вызова exit все непосредственные процессы-по- томки процесса, "кончающего жизнь самоубийством", становятся непосредс- твенными процессами-потомками некоторого процесса, который может быть назван всеобщим предком. Поэтому для любого процесса, кроме всеобщего предка, который является бессмертным, всегда существует процесс-предок, который позаботится о его бренных останках. 1 По поверьям западной Индии зомби — это телесная оболочка умершего человека, которая благодаря волшебной силе получила возможность двигаться и совершать раз- личные дейстивия. (Прим, пер.) 172
Завершение процесса-потомка приводит к пробуждению и переходу из состояния ожидания в состояние выполнения процесса-предка, а им в данном случае является интерпретатор команд shell. Процесс-предок получает инфор- мацию о том, какой из его потомков завершил свое существование, и код завершения этого процесса. Процесс, завершающий свое существование, ука- зывает код своего завершения в качестве аргумента системного вызова exit. Поэтому системные вызовы ехес и exit можно уподобить операторам .обраще- ния к функции и возврата из функции, имеющимся в языке программирова- ния Си. После того как процессу-предку стал известен код завершения процесса-потомка, с тем немногим, что осталось от процесса-потомка, можно покончить. Именно это и делает процесс-предок, уничтожая все следы сущест- вования завершившегося процесса-потомка. Теперь, когда от завершившегося процесса-потомка не осталось уже ника- ких следов, процесс-предок, а им в данном случае является интерпретатор команд shell, продолжает свое выполнение, т. е. выводит на терминал очеред- ной промптер, являющийся признаком готовности интерпретатора команд shell к приему следующей командной строки. 8.3. ПОРОЖДЕНИЕ ПРОЦЕССОВ Если Вы хотите попытаться разобраться в том, как используются систем- ные вызовы, то Вы должны быть готовы к тому, чтобы читать фрагменты про- грамм на языке программирования Си. Хотя системные вызовы могут исполь- зоваться и в программах, написанных на языке программирования Фор- тран 77, однако стало уже общепринятым иллюстрировать системные вызо- вы, применяя для этой цели язык программирования Си. Хотя конкретные действия, производимые в ходе выполнения системных вызовов, в значитель- ной степени зависят от типа используемой ЭВМ, результаты выполнения систем- ных вызовов оказываются в конце концов практически одинаковыми вне за- висимости от типа используемой ЭВМ. При издании процессом системного вы- зова управление передается ядру ОС UNIX. Для того, чтобы передать управ- ление ядру ОС UNIX, практически всегда требуется употребить некоторую специальную машинную команду. Для ЭВМ семейства PDP-11 такой специаль- ной машинной командой является команда TRAP, а для ЭВМ Interdata — ко- манда SVC. В каждой ЭВМ эта команда, как правило, называется по-своему. Задачей тех, кто занимался реализацией ОС UNIX, было спрятать детали реа- лизации с помощью библиотечных функций, поскольку синтаксически изда- ние системного вызова эквивалентно обращению к библиотечной функции. Наша задача состоит в рассмотрении использования этих библиотечных функций. 8.3.1. ПРИМЕР ИСПОЛЬЗОВАНИЯ СИСТЕМНОГО ВЫЗОВА fork Системный вызов fork занимает центральное место среди прочих систем- ных вызовов, имеющихся в ОС UNIX. Породить новый процесс можно толь- ко с помощью системного вызова fork. 173
Ниже приведена программа на языке программирования Си,в которой ис- пользуется системный вызов fork. Системный вызов fork возвращает различ- ные значения в процесс-предок и в процесс-потомок. Именно по значению, возвращаемому системным вызовом fork, каждый из них в состоянии опре- делить, кто он такой. Значением, возвращаемым системным вызовом fork в процесс-потомок, является 0, а значением, возвращаемым системным вы- зовом fork в процесс-предок, — идентификатор процесса-потомка, т. е. его уникальный номер. Идентификатор процесса представляет собой целое число в диапазоне от 1 до 30 0001, с помощью которого процесс однозначно иденти- фицируется ядром ОС UNIX. Та часть ядра ОС UNIX, которая ответственна за генерацию идентификаторов процессов для вновь порождаемых процессов, гарантирует уникальность генерируемых идентификаторов процессов, что означает невозможность возникновения ситуации, при которой два или более одновременно существующих процесса имеют одинаковые идентификаторы процесса. Если непрерывное функционирование ОС UNIX продолжается настолько долго, что было порождено точно 30000 процессов, то для следующего порожденного процесса будет сгенерировано значение идентифи- катора процесса, равное 1. Прежде чем установить сгенерированное значение в качестве идентификатора порожденного процесса, будет осуществлена проверка на отсутствие процесса с тем же идентификатором процесса. Если такого процесса нет, то сгенерированное значение идентификатора процесса устанавливается в качестве значения идентификатора процесса для порожден- ного процесса. В противном случае сгенерированное значение идентификато- ра процесса увеличивается на 1 и вышеописанные действия повторяются. Приведенная ниже программа на языке программирования Си иллюстри- рует также проверку того, как завершилось выполнение системного вызова fork. Дело в том, что выполнение различных системных вызовов но целому ряду причин может завершаться аварийно. В случае аварийного завершения системные вызовы, как правило, возвращают значение, равное —1. Таким об- разом, значение —1, возвращаемое некоторым системным вызовом, обычно служит индикатором того, что произошло аварийное завершение выполнения этого системного вызова. Несколько позднее мы вернемся к этому вопросу и рассмотрим его подробнее. \* пример использования системных вызовов fork, wait и exit*\ main(){ mt pid,status,died; switch(pid = fork()){ case -1: printff'Can't fork\n”); exit( — 1); case 0: \* процесс-потомок *\ printf ("I am the chi ld\n") ; exit(3); 1 Иногда от 1 до 32 767. (Прим, ред.) 174
default: /* процесс-предок */ died = wan(&status). } printf("child was %d n '.pid); pnntf("%d died'.n”,died); printf(''exit value %d\n",status)>8); printf("exit status %d\n",status & 0377); } В приведенном выше примере значение, возвращаемое системным вызо- вом fork, присваивается целой переменной pid и используется затем для при- нятия решения о последующих действиях. case -1: Если значение, возвращаемое системным вызовом fork, равно —1, то по тем или иным причинам произошло аварийное завершение системного вызо- ва fork; в этом случае на стандартный вывод выводится сообщение об ошиб- ке, а затем процесс-предок завершает свое существование с помощью систем- ного вызова exit, аргумент которого, равный —1, служит индикатором ава- рийности завершения этого процесса, поскольку в этом случае не происходит порождения процесса-потомка. case 0: Если значение, возвращаемое системным вызовом fork, равно 0, то выпол- няется процесс-потомок; в этом случае на стандартный вывод выводится сообщение о том, что это процесс-потомок, а затем процесс-потомок заверша- ет свое существованиеспомощью системного вызова exit, аргумент которого, равный 3, впоследствии передается процессу-предку в качестве кода заверше- ния процесса-потомка. default: Если значение, возвращаемое системным вызовом fork, не равно ни —1, ни 0, то выполняется процесс-предок; в этом случае с помощью системного вызова wait осуществляется ожидание завершения процесса-потомка. По за- вершении процесса-потомка процесс-предок пробуждается и в качестве значе- ния системного вызова wait получает идентификатор завершившегося про- цесса-потомка, что позволяет процессу-предку, имеющему более одного про- цесса-потомка, определить, какой же из его процессов-потомкоз завершился. Аргументом системного вызова wait является указатель на целую перемен- ную status, которая после завершения выполнения этого системного вызова содержит в старшем байте код завершения процесса-потомка, передаваемый им в качестве аргумента системного вызова exit, а в младшем байте — инди- катор причины завершения процесса-потомка, устанавливаемый ядром ОС UNIX. Если процесс-потомок завершил свое существование самоубийством с помощью системного вызова exit, то значение младшего байта переменной 175
status будет_равно 0. Если же при выполнении процесса-потомка возникли какие-либо неполадки и ядро ОС UNIX было вынуждено вследствие этого осуществить аварийное завершение его существования, то младший байт пе- ременной status будет содержать код, объясняющий причину такого аварий- ного завершения. Во избежание путаницы будем называть значение, содержа- щееся в старшем байте переменной status, — пользовательским кодом завер- шения процесса, а значение, содержащееся в младшем байте переменной status, — системным кодом завершения процесса. Процесс-предок осуществляет вывод на стандартный вывод целого ряда значений: идентификатора процесса порожденного им процесса-потомка, идентификатора процесса завершившегося процесса-потомка (который, оче- видно, совпадает с идентификатором процесса порожденного им процесса- потомка) , пользовательского кода завершения процесса-потомка, передава- емого последним в качестве аргумента системного вызова exit, и системного кода завершения процесса-потомка. Если процесс порождает более одного процесса-потом к а и его интересует судьба каждого из них, то системный вызов wait должен быть издан им столько раз, сколько порождено процессов-потомков. Как мы уже говорили, процес- сы не имеющие непосредственного процесса-предка, наследуются всеобщим предком — процессом, порождаемым в ходе раскрутки ОС UNIX, которая следует непосредственно за ее загрузкой. Значение идентификатора этого процесса всегда равно 1, а его функцией является постоянное ожидание за- вершения процессов-сирот, т. е. процессов, непосредственные процессы-пред- ки которых уже завершили свое выполнение. Всеобщий предок выполняет чрезвычайно важную функцию: он уничтожает завершившиеся процессы, на- ходящиеся в состоянии зомби. Если бы всеобщий предок отсутствовал, то функционирование ОС UNIX стало бы невозможным уже вскоре после ее запуска1 в результате накопления завершившихся процессов, находящихся в состоянии зомби. Если в своих программах Вы начнете интенсивно (много- кратно) использовать системный вызов fork, не используя при этом систем- ный вызов wait для уничтожения завершившихся процессов, находящихся в состоянии зомби, то, скорее всего, столкнетесь с неприятностями. Возможно, что столь необходимый Вам системный вызов fork в какой-то момент нельзя будет выполнить. Это связано с тем, что в ОС UNIX каждому пользо- вателю разрешено иметь одновременно определенное число процессов, фик- сируемое в процессе генерации ОС UNIX, а при подсчете в число процессов, имеющихся в данный момент у некоторого пользователя, включаются также завершившиеся процессы, находящиеся в состоянии зомби2. 1 Запуск ОС UNIX описывается в гл. 10. (Прим, ред.) ’ В версии 6 ОС UNIX контролируется лишь суммарное число выполняющихся в дан- ный момент процессов, поэтому один из пользователей может, например, лишить воз- можности работы всех прочих пользователей. {Прим, ред.) 176
8.3.2. ОШИБКИ, ВОЗНИКАЮЩИЕ ПРИ ВЫПОЛНЕНИИ СИСТЕМНЫХ ВЫЗОВОВ Как Вы уже поняли из приведенного выше примера, выполнение систем- ных вызовов может завершаться аварийно, а индикатором аварийного завер- шения выполнения системных вызовов, как правило, служит возвращаемое ими значение, равное — 1, Однако зачастую необходимо выяснить причину аварийного завершения выполнения некоторого системного вызова, С этой целью та часть ядра ОС UNIX, которая осуществляет отработку системных вызовов, обеспечивает установку кода, объясняющего причину аварийного завершения выполнения системных вызовов в качестве значения внешней переменной ermo. Значения этих кодов вместе с их символическими названи- ями приведены в приложении Д. Кроме того, в состав библиотеки стандарт- ных функций входит библиотечная функция реггог, обеспечивающая вывод на стандартный протокол полного текста сообщения об ошибке, соответст- вующего текущему значению внешней переменной еппо. При использовании библиотечной функции реггог Вы должны помнить о том, что с помощью этой библиотечной функции осуществляется вывод на стандартный прото- кол полного текста сообщения о последней случившейся в ходе выполнения какого-либо системного вызова ошибке; благополучное же завершение вы- полнения системных вызовов не приводит к сбросу текущего значения внеш- ней переменной ermo. Следующая программа на языке программирования Си иллюстрирует использование внешней переменной еппо и библиотечной функции реггог. Аварийное завершение выполнения системного вызова stime гарантировано, если только Вы не являетесь привилегированным поль- зователем. extern errno; main(){ int res; res = stime(O); printf("returned %d\n",res); printf("errno is %d\n",errno); perror('’tried to set time"); } Попробуйте воспроизвести этот пример. Если Вам удастся с помощью этого примера установить время и дату, то мы Вам не завидуем, представляя себе все те шишки, которые на Вас свалятся, так как Вы установили одно из са- мых дурацких значений времени и даты. В заключение отметим, что в нашей повседневной работе мы очень часто используем библиотечную функцию реггог и считаем ее крайне полезной. 177
8.4. АТРИБУТЫ ПРОЦЕССОВ Как мы уже говорили, процесс является чем-то большим, чем просто про- грамма. Большинство атрибутов процесса наследуется им от своего предка. К таким атрибутам относятся открытые файлы, идентификатор пользователя, текущий каталог и т. д. Давайте рассмотрим некоторые из этих атрибутов. 8.4.1. НАСЛЕДУЕМЫЕ ФАЙЛЫ Все файлы, которые были открыты в процессе, издавшем системный вы- зов fork, будут унаследованы порожденным в результате выполнения систем- ного вызова fork процессом-потомком, если только не были предприняты специальные действия, предотвращающие это {что бывает крайне редко). В результате доступ к этим файлам возможен как из процесса-предка, так и из процесса-потомка. Если какой-либо из этих процессов решит закрыть один из таких файлов, то доступа к файлу лишится только он сам, для дру- гого процесса этот файл по-прежнему останется открытым. Необходимо от- метить, что число процессов, имеющих доступ к таким файлам, может быть и больше двух, например в случае порождения более одного процесса-потомка. Та кие файлы мы будем называтьнаследуемыми файлами. Особенностью насле- дуемых файлов является то, что все процессы, имеющие доступ к такому файлу, разделяют единственный указатель чтения-записи1. Для наследуемых файлов это означает, что если один из процессов изменяет значение указателя чтения-записи некоторого файла, то значение указателя чтения-записи этого файла будет изменено для всех процессов, имеющих доступ к нему. Как пра- вило, это не вызывает никаких проблем, однако для полноты изложения мы должны были упомянуть об этом. Не объяснив, что же собой представляют указатели чтения-записи, мы ухитрились сделать замечание об их использова- нии в случае работы с наследуемыми файлами. Объяснение того, что собой представляют указатели чтения-записи, Вы найдете в этой главе немного позд- нее — в параграфе, посвященному вводу-выводу. В заключение отметим, что наиболее часто наследуемыми файлами являются лишь стандартный ввод,стан- дартный вывод и стандартный протокол. В первую очередь Вы захотите разде- лять между процессами именно эти файлы. 8.4.2. ИДЕНТИФИКАТОР ПОЛЬЗОВАТЕЛЯ И ИДЕНТИФИКАТОР ГРУППЫ В ОС UNIX каждый процесс имеет удостоверение личности, определяющее его права на доступ к различным файлам. Таким удостоверением личности процесса являются идентификатор пользователя и идентификатор группы, определяющие пользователя, от имени которого выступает процесс, и группу, членом которой он является. При обсуждении вопросов, связанных смеха- 1 Это не то же самое, что указатель на файл, используемый а стандартной библиотеке ввода-вывода. ПВ
низмом защиты, используемым файловой системой ОС UNIX, мы уже гово- рили об этом. Напомним, что ядро ОС UNIX, прежде чем осуществить откры- тие файла, производит проверку допустимости этого действия. Установка идентификатора пользователя и идентификатора группы процес- са, в рамках которого выполняется интерпретатор команд shell, производится в процессе Вашего входа в ОС UNIX. Идентификатор пользователя и иденти- фикатор группы представляют собой целые числа. При входе в ОС UNIX их значения выбираются программой login из соответствующего места файла /etc/passwd, содержащего досье на всех зарегистрированных в ОС UNIX поль- зователей. Ядро ОС UNIX ничего не знает об именах, под которыми поль- зователи зарегистрированы в ОС UNIX, и об именах групп. Ядро ОС UNIX имеет дело только с номерами пользователей и номерами групп, т. е. с иденти- фикаторами пользователей и идентификаторами групп. Идентификатор поль- зователя и идентификатор группы, установленные для процесса, в рамках которого выполняется интерпретатор команд shell, передаются этим процес- сом своим процессам-потомкам по наследству, а процессы-потомки, в свою очередь, передают их по наследству своим процессам-потомкам и т. д. Таким образом идентификатор пользователя и идентификатор группы, которые были установлены для процесса, в рамках которого выполняется интерпре- татор команд shell, будут служить удостоверением личности для всех без иск- лючения процессов, в рамках которых выполняются вызываемые Вами на выполнение программы. Поэтому в ОС UNIX чрезвычайно_просто установить принадлежность процесса тому или иному пользователю. На самом деле нарисованная здесь картина является несколько идеализированной. Это Выло сделано специально с целью облегчить понимание сути понятий идентифика- тора пользователя и идентификатора группы. Немного позднее Вы узнаете, как все обстоит на самом деле. А сейчас несколько замечаний о связи имени, под которым пользователь зарегистрирован в ОС UNIX, и идентификатора пользователя. Имя, под которым пользователь зарегистрирован в ОС UNIX, в некотором смысле является избыточным, поскольку ядро ОС UNIX даже не знает о его существовании. С точки зрения ядра ОС UNIX каждый пользо- ватель однозначно идентифицируется с помощью своего идентификатора пользователя. Команда 1s, позволяющая Вам получить имя, под которым за- регистрирован в ОС UNIX владелец того или иного файла, на самом деле оп- ределяет идентификатор пользователя и идентификатор группы, а затем, используя досье пользователей, определяет соответствующие этим идентифи- каторам имена. Для того чтобы выяснить текущие значения идентификатора пользователя и идентификатора группы, можно воспользоваться системными вызовами getuid и getgid соответственно. Ниже приводится программа на языке прог- раммирования Си, иллюстрирующая использование системных вызовов getuid и getgid: 179
main(){ int uid,gid; uid = getuid(); gid = getgidQ; pnntff'uid = %d, gtd = %d\n",utd,gid); } А теперь рассмотрим, как на самом депе обстоит дело с идентификатора- ми пользователей и идентификаторами групп. В действительности с каж- дым процессом связано два идентификатора пользователя и два иденти- фикатора группы. Действующий идентификатор пользователя и действую- щий идентификатор группы используются при проверке разрешения на доступ к файлам. Реальный идентификатор пользователя и реальный иден- тификатор группы определяют пользователя, от имени которого высту- пает процесс, и группу, членом которой он является. Мы надеемся, что Вы в первом приближении поняли разницу между действующими иденти- фикаторами и реальными идентификаторами. Для того чтобы выяснить текущие значения реального идентификатора пользователя и реального идентификатора группы, необходимо воспользоваться системными вызо- вами getuid и getgid. Для того чтобы выяснить текущие значения дей- ствующего идентификатора пользователя и действующего идентификатора группы, необходимо воспользоваться системными вызовами geteuid и get egid, которые аналогичны системным вызовам getuid и getgid1. В большинстве случаев значения реальных и действующих идентификато- ров совпадают, т. е. при проверке разрешения на доступ к файлам, как правило, используются реальные идентификаторы, поскольку их значе- ния совпадают со значениями действующих идентификаторов. Зачем же тогда в ОС UNIX каждый процесс характеризуется и реальными иденти- фикаторами, и действующими идентификаторами? В основе ответа на вопрос о причине наличия в процессе действующих идентификаторов и реальных идентификаторов лежит тот факт, что зна- чения реальных идентификаторов и значения действующих идентифика- торов могут отличаться друг от друга. Некоторые файлы, содер- жащие выполняемые программы, имеют специальные признаки, обеспе- чивающие при вызове на выполнение программы, хранящейся в таком файле, установку действующего идентификатора пользователя и действую- щего идентификатора группы равными идентификатору владельца и иден- тификатору группы этого файла. Для указания на необходимость переуста- 1 В некоторых версиях ОС UNIX значение и реальных идентификаторов, и дей- ствующих идентификаторов можно получить с помощью системных вызовов getuid и getgid, возвращающих реальный идентификатор в младшем байте возвра- щаемого значения, а действующий идентификатор — в старшем байте возвращаемого значения, (Прим, ред.} 180
новки действующих идентификаторов используются два бита в коде за- щиты файла, о которых мы уже упоминали в гл. 5, посвященной описанию файловой системы ОС UNIX. Установка в 1 бита setuid (4000) приводит к переустановке действующего идентификатора пользователя, а установка в 1 бита setgid (2000) — к переустановке действующего идентификатора группы. Владелец файла может устанавливать эти биты в 1 и сбрасывать их в 0 с той же легкостью, как и биты, задающие собственно код защиты файла. Установка в 1 в коде защиты файла одного из этих двух битов или их обоих приводит к тому, что процесс, в рамках которого выполняет- ся программа, хранящаяся в этом файле, с точки зрения доступа к файлам будет выступать не от имени пользователя, вызвавшего эту программу на выполнение, а от имени пользователя, являющегося владельцем файла, в котором хранится эта программа. Тем самым процесс посредством исполняющейся в его рамках программы получает возможность осуще- ствлять доступ к файлам, к которым имеет доступ владелец файла, в ко- тором хранится выполняющаяся в рамках процесса программа, но, быть может, не имеет доступа пользователь, вызвавший эту программу на вы- полнение. Установка в 1 битов, обеспечивающих переустановку действующих иден- тификаторов, используется для реализации более сложной схемы защиты файлов по сравнению со стандартной схемой защиты, используемой в файловой системе ОС UNIX. В этом случае имеется возможность обеспечить контролируемый доступ к файлам только с помощью набора программ, обеспечивающих такой доступ. Тем самым пользователь может быть лишен возможности явным образом осуществлять чтение из файла или запись в файл, но при этом он может получать определенную информацию из файла с помощью некоторого набора программ и заносить некоторую информа- цию в файл с помощью некоторого набора программ. В качестве примера программы, использующей биты переустановки дей- ствующих идентификаторов, рассмотрим команду passwd. Владельцем фай- ла, в котором хранится программа, обеспечивающая выполнение команды passwd, является пользователь, имеющий право на запись в файл /etc/passwd- файл, содержащий досье пользователей. В коде защиты файла, в котором хранится программа, обеспечивающая выполнение команды passwd, бит setuid установлен в 1. Будучи вызвана на выполнение, команда passwd проверяет, имеете ли Вы право осуществить смену пароля, допустимость па- роля с криптографической точки зрения, правильно ли Вы ввели новый пароль вторично, и, если все в порядке, заносит новый пароль в закодиро- ванном виде в соответствующее место в файл /etc/passwd. Как видите, идея переустановки действующих идентификаторов крайне проста и может быть использована каждым. Те процессы, которые по каким-либо причинам не удовлетворены теку- щими значениями своих действующих идентификаторов, имеют возмож- ность изменить их значения с помощью системных вызовов setuid и setgid. Процессы, в рамках которых выполняются программы, вызванные на вы- 181
полнение непривилегированным (рядовым) пользователем, имеют право переустановить значения своих действующих идентификаторов только в со- ответствии со значениями своих реальных идентификаторов1. Привилеги- рованный пользователь с помощью системных вызовов setuid и setgid имеет право устанавливать любые значения действующих идентификаторов и реальных идентификаторов в процессах, выступающих от его имени. В про- цессах, выступающих от имени привилегированного пользователя, значение действующего идентификатора пользователя равно 0. Поскольку процесс, в рамках которого выполняется программа login, выступает от имени приви- легированного пользователя, то понятно, что для него не составляет ника- кого труда переустановить значения действующих идентификаторов и ре- альных идентификаторов в соответствии с теми, которые хранятся в соот- ветствующей Вам строке досье пользователей, непосредственно перед тем, как с помощью системного вызова ехес он сменит программу, определяю- щую его поведение, на программу, соответствующую интерпретатору ко- манд shell. Только что мы объяснили, каким образом Вы становитесь самим собой при входе в ОС UNIX. Иногда процессу, выступающему от имени привилегированного пользо- вателя (т. е. процессу, значение действующего идентификатора пользователя которого равно 0), бывает необходимо узнать, какие права доступа к фай- лам обеспечиваются в соответствии с реальными идентификаторами. С этой целью процесс может воспользоваться системным вызовом access, который обеспечивает проверку разрешения на доступ к файлу в соответствии не со значениями действующих идентификаторов, а со значениями реальных иден- тификаторов. Ниже приводится пример использования системного вызова access: access("xyz",mode); Если к файлу xyz может быть осуществлен доступ в соответствии с режимом, задаваемым аргументом mode, то значение, возвращаемое системным вызо- вом access равно 0. В противном случае значение, возвращаемое системным вызовом access, равно —1. Аргумент mode задает режим доступа у файлу следующим образом: 4 — чтение, 2 — запись, 1 — выполнение. Вы должны помнить о том, что наличие у файла разрешения на выполнение является не- обходимым, но не достаточным условием для того, чтобы его содержимое можно было выполнить как программу, поскольку для этого также нужно, чтобы содержимым файла являлась выполняемая программа. Наличие у процессов действующих идентификаторов наряду с реальными идентифи- каторами может порождать довольно интересную проблему в тех случаях, когда значения действующих идентификаторов и значения реальных иден- тификаторов не совпадают. При этом может оказаться, что процесс не имеет 1 В противном случае имелась бы возможность нарушения существующего меха- низма защиты. 182
доступа к файлам, доступ к которым возможен, если иметь в виду реальные идентификаторы. Это результат того, что при проверке разрешения на дос- туп к файлам используются действующие идентификаторы, 8.5. КОМАНДЫ И СИСТЕМНЫЕ ВЫЗОВЫ 8.5.1, ТЕКУЩИЙ КАТАЛОГ Каждый процесс в ОС UNIX имеет свой текущий каталог. С понятием текущего каталога Вы уже знакомь! — это каталог, начиная с которого осуществляется поиск файла в случае указания относительного полного имени файла, т. е. в случае, когда полное имя файла начинается не с симво- ла /. Программа login устанавливает для вызываемого ею интерпретатора команд shell в качестве текущего каталога Ваш основной каталог пользо- вателя, имя которого храниться в соответствующей строке файла, содер- жащего досье пользователей1. Процесс может сменить текущий каталог с помощью системного вызова chdir. Следующий пример иллюстрирует уста- новку в качестве текущего каталога для процесса каталога /usr/fred chdirf’/usr/fred"); Естественно, что смена текущего каталога будет осуществлена только в том случае, если процесс имеет разрешение на установку требуемого каталога в качестве текущего каталога. Если значение, возвращаемое системным вы- зовом chdir, равно —1, то произошло аварийное завершение его выполне- ния. Причиной аварийного завершения выполнения системного вызова chdir может быть то, что издавший его процесс не имеет разрешения на установку указанного каталога в качестве текущего каталога, или то, что каталог, указанный для установки в качестве текущего каталога, не су- ществует. Текущий каталог является атрибутом процесса, наследуемым процессом- потомком от процесса-предка. Поэтому для выполнения команды cd ин- терпретатор команд shell использует системный вызов chdir. 8.5.2. МАСКА КОДА ЗАЩИТЫ ФАЙЛА Еще одним атрибутом процесса, наследуемым процессом-потомком от процесса-предка, является маска кода защиты файла. Большинство команд в ОС UNIX совершенно не заботится о защите создаваемых ими файлов. Это означает, что, как правило,файлы,создаваемые командами QC UNIX, имеют разрешение на доступ к ним по чтению для любого пользователя, независи- мо от того, к какой категории пользователей он относится, и разрешение на доступ по записи только для владельца. В подавляющем большинстве случаев такой подход является наиболее удобным, поскольку освобождает 1 Вы может выяснить имя Вашего основного каталога пользователя, так как оно является значением переменной НОМЕ интерпретатора команд shell. 183
пользователей, не заботящихся о сокрытии содержимого своих файлов, от необходимости каждый раз явно задавать код защиты файла: число таких пользователей, как правило, намного превышает число пользователей, имеющих необходимость скрывать содержимое своих файлов. Тем не менее, если по тем или иным причинам Вы озабочены вопроса- ми защиты файлов уже к моменту их создания, то можете воспользоваться системным вызовом umask, обеспечивающим установку значения маски кода защиты файла. Маска кода защиты файла позволяет маскировать раз- ряды кода защиты файла. Следующий пример иллюстрирует запрещение какого бы то ни было доступа к создаваемым впоследствии файлам для пользователей, являющихся по отношению к файлу прочими пользовате- лями: umask(7); В результате выполнения этого системного вызова значения трех младших битов кода защиты файла для всех создаваемых впоследствии файлов бу- дет равно 0 (7g = 1112) Таким образом, установка в 1 некоторого бита маски кода защиты файла приводит к установке в 0 соответствующего би- та кода защиты файла для всех создаваемых впоследствии файлов, что в свою очередь, приводит к запрету соответствующего типа доступа для со- ответствующей этому биту категории пользователей. Поскольку маска кода защиты файла является атрибутом процесса, наследуемым процессом-потомком от процесса-предка, тодля выполнения команды umask интерпретатор команд shell использует системный вызов umask. Ни в коем случае не следует путать команды и системные вы- зовы. Команды обрабатываются интерпретатором команд shell, который для их выполнения использует системные вызовы. Хотя названия команд и системных вызовов могут совпадать, между ними лежит пропасть. 8.6. СИСТЕМНЫЙ ВЫЗОВ ехес Король умер. Да здравствует Король! Итак, Вы познакомились с целым рядом атрибутов, наследуемых про- цессами-потомками от процессов предков, более того, научились изменять некоторые из этих атрибутов по своему усмотрению. Если Вы устали, то сейчас самое время сделать перерыв для того, чтобы усвоить полученную информацию. Если же Вы не устали, то продолжим. Давайте теперь рас- смотрим, что же происходит при вызове программ на выполнение. Как Вы уже знаете, при вызове программ на выполнение сначала по- рождается новый процесс — процесс, в рамках которого будет выпол- няться вызываемая на выполнение программа, а затем этот новый процесс осуществляет смену программы, определяющей его поведение. Смена про- граммы, определяющей поведение процесса, осуществляется с помощью 184
одной из форм системного вызова ехес (системный вызов ехес имеет несколько различных форм}. Еще раз напомним, что как правило, смена программы, определяющей поведение процесса, осуществляется после порождения нового процесса с помощью системного вызова fork. Ниже приведена короткая программа на языке программирования Си, в кото- рой не используется системный вызов fork, а просто осуществляется смена программы, определяющей поведение процесса, в рамках которого она сама выполняется, на программу echo; execl("/bin/echo","echo","hello mike",O); printf("The exec failed\n"); Если выполнение системного вызова ехес (а в приведенном выше приме- ре он использован в форме ехес!) завершится нормально, т. е. не аварийно, то оператор, в котором производится обращение к библиотечной функции printf, никогда не будет выполнен, поскольку к моменту завершения вы- полнения системного вызова ехес поведение процесса будет определяться не программой, содержащей этот оператор, а программой echo. Первый ар- гумент системного вызова ехес, использованного в форме execl, задает имя файла, содержащего программу, которую требуется вызвать на выпол- нение. Все последующие аргументы системного вызова ехес, использован- ного в форме execl, передаются вызываемой на выполнение программе. По установленному соглашению* 1 второй аргумент системного вызова ехес, использованного в форме execl, является именем программы, которая вызывается на выполнение с помощью этого системного вызова3. При использовании системного вызова ехес в форме execl список его ар- гументов должен завершаться нулем, как это показано в приведенном выше примере. Необходимо помнить, что в результате использования системного вызо- ва ехес действующий идентификатор пользователя и действующий иденти- фикатор группы могут изменяться, если в коде защиты файла, содержаще- 1 Принятие такого соглашения обусловлено механизмом передачи в программу аргументов, указываемых в командной строке, с помощью которой производится вызов этой программы на выполнение. Второй аргумент системного вызова ехес, ис- пользованного а форме execl, должен являться именем файла, в котором хранится программа, которая вызывается на выполнение с помощью этого системного вызова, поскольку он является первым передаваемым в вызываемую при выполнении прог- рамму аргументом и передается в эту программу как argv [О]. (Прим. ред.) 1 Именем файла, содержащего программу, которую требуется вызвать на выпол- нение в приведенном выше примере, являлось /bin/echo, поскольку, если только, ко- нечно Вы не используете какую-нибудь экзотическую версию ОС UNIX или подобной ей операционной системы, программа, обеспечивающая выполнение команды echo,со- держится именно в этом файле. 185
го вызываемую на выполнение программу, установлены соответствующие биты. Иногда число аргументов, которые требуется передать в вызываемую на выполнение программу, заранее не известно. В таких случаях можно ис- пользовать системный вызов ехес в форме execv. Если системный вызов ехес используется в форме execv, то его первый аргумент задает имя фай- ла, содержащего программу, которую требуется вызвать на выполнение, а его второй аргумент является указателем на массив указателей на аргу- менты, которые требуется передать вызываемой на выполнение программе. Массив указателей на аргументы, которые требуется передать вызываемой на выполнение программе, должен завершаться элементом, содержащим в качестве своего.значения 0. Ниже приведена программа на языке програм- мирования Си, которая функционально эквивалентна приведенной выше программе на языке программирования Си: /* это инициализированный массив указателей на строки символов */ char *arglist[] ={ "echo", "hello mike", 0}; main(){ execv("/bin/echo",arglist); printf ("The exec failed\n"); } Возможность порождения нового процесса и выполнения в рамках этого процесса новой программы является одним из самых сильных свойств, присущих ОС UNIX. Предположим, что Вам необходимо создать каталог, создать в этом каталоге какие-либо файлы, провести ряд операций запи- си в эти файлы и ряд операций чтения из этих файлов, а затем удалить эти файлы и удалить сам каталог. Рядовой (непривилегированный) пользова- тель не в состоянии непосредственно решить такую задачу, поскольку соз- давать и удалять каталоги разрешено только привилегированному поль- зователю, так как для проведения этих операций необходимо осуществлять проверку на то, не будет ли в результате их проведения нарушена целост- ность файловой системы. Тем не менее в ОС UNIX непривелигированный пользователь может решить такую задачу. Например, для того, чтобы соз- дать каталог непривилегированному пользователю, достаточно с помощью системного вызова fork породить новый процесса затем с помощью сис- темного вызова ехес сменить программу, определяющую поведение этого процесса, на программу, содержащуюся в файле /bin/mkdir, в коде защи- ты которого установлен бит, обеспечивающий переустановку действующего идентификатора в нуль, что обеспечивает переустановку действующего идентификатора пользователя в 0, что обеспечивает выполнение этой прог- раммы от имени привилегированного пользователя. При этом пользователь 186
освобожден от необходимости даже задумываться о том, что нужно осу- ществлять какие-то проверки. О результате завершения выполнения этой программы пользователь узнает по коду ее завершения. В некоторых слу- чаях удобнее использовать библиотечную функцию system, если, конечно, с помощью этой библиотечной функции можно решить стоящую перед Вами задачу. Описание библиотечной функции system можно найти в Руко- водстве и в приложении Г. Ну, а уж если Вы хотите решить подобную за- дачу наилучшим образом, то следует, не используя файлы, замкнуть стан- дартный вывод одних программ на стандартный ввод других программ, применяя для этой цели каналы. В этом случае Вам необходимо изучить системный вызов pipe, обеспечивающий создание канала для коммуника- ций между двумя процессами. Воспользуйтесь для этого Руководством. Обе продемонстрированные нами здесь формы использования систем- ного вызова ехес являются упрощенными версиями двух других форм системного вызова ехес: execle и execve, которые, однако, слишком слож- ны, для того чтобы их здесь рассматривать. Короче говоря, последние две формы системного вызова ехес передают новой программе еще один спи- сок аргументов, называемый окружением (environment). Для того чтобы познакомиться с ними, изучите разд. 2 Руководства. 8.7. СИГНАЛЫ Процессы в ОС UNIX могут принимать сигналы. Механизм сигналов позволяет процессам реагировать на различные события, которые могут произойти в ходе выполнения процесса внутри него или во внешнем для него мире. Примером сигнала, являющегося результатов события, случив- шегося во внешнем для процесса мире, является сигнал SIGINT, который является результатом нажатия клавиши, соответствующей специальному символу INTERRUPT. При нажатии Вами клавиши, соответствующей спе- циальному символу INTERRUPT, сигнал SIGINT посылается всем процес- сам, в рамках которых выполняются программы, вызванные на выполне- ние с Вашего терминала. Примерами сигналов, являющихся результатами событий, которые могут произойти в ходе выполнения процесса внутри него, служат попытка выполнения запрещенной команды и попытка обращения к несуществующей памяти. Сигналы, являющиеся результа- тами событий, которые могут случиться в ходе выполнения процесса внутри него, фактически являются сигналами того, что в ходе выполнения процесса возникло внутреннее прерывание. Реакция процесса на принимаемый сигнал зависит от того, как сам про- цесс ранее определил свое поведение в случае приема им данного сигнала. Процесс может объявить, что он будет игнорировать данный сигнал. При этом в худшем случае, в результате того, что процесс примет данный сигнал, может произойти преждевременное завершение выполнения неко- торых системных вызовов. К ним относятся системные вызовы read и write для работы со специальными файлами, которые соответствуют медленным 187
устройствами, какими, например, являются терминалы, а также систем- ные вызовы pause и wait. Процесс может не объявлять ничего о своей реакции на данный сигнал. В этом случае в результате получения процессом данного сигнала его вы- полнение будет немедленно прервано, и его дальнейшая судьба определя- ется тем, какова стандартная реакция ядра ОС UNIX на данный сигнал, поскольку именно ядро ОС UNIX в этом случае будет принимать решение о дальнейших действиях. Как правило, в таких случаях выполнение про- цесса завершается, причем иногда этому предшествует проведение дам- па оперативной памяти в файл соте в текущем в данный момент для процесса каталоге. Впоследствии по содержимому файла соте можно бу- дет установить причину завершения выполнения процесса. Процесс может объявить, что он будет перехватывать данный сигнал. Перехват сигнала процессом означает, что в результате получения данного сигнала процесс будет выполнять некоторую последовательность определя- емых самим процессом действий, являющуюся реакцией процесса на дан- ный сигнал. Для задания процессом типа реакции на некоторый сигнал служит сис- темный вызов signal, первый аргумент которого определяет сигнал, реак- ция на который будет задана с помощью второго аргумента этого систем- ного вызова. Число сигналов в ОС UNIX ограничено, причем каждый сиг- нал имеет свой уникальный номер. Номера сигналов и соответствующие этим номерам мнемоники приведены в приложении Д. Давайте посмотрим, что происходит при нажатии клавиши, соответст- вующей специальному символу INTERRUPT. Обычно процессы не забо- тятся о том, чтобы перехватывать или игнорировать этот сигнал; поэтому результатом того, что процесс примет данный сигнал, будет немедленное завершение его выполнения, что обеспечивается стандартной реакцией ядра ОС UNIX на данный сигнал. Интерпретатор команд shell игнорирует дан- ный сигнал, поскольку если бы это было не так, то Вам пришлось бы каж- дый раз после нажатия клавиши, соответствующей специальному символу INTERRUPT, заново входить в ОС UNIX, что было бы не слишком удобно для Вас. Процессы, в рамках которых выполняются программы, вызван- ные на выполнение в асинхронном режиме, также игнорируют этот сигнал. Об этом позаботился интерпретатор команд shell, воспользовавшись тем, что тип реакции процесса на сигнал для игнорируемых процессом сигна- лов не изменяется в результате выполнения системного вызова ехес. При перехвате процессом сигналов тип реакции на сигнал переустанавливает- ся на стандартный в результате выполнения системного вызова ехес, по- скольку сохранять его не имеет смысла. Ниже приведена программа на языке программирования Си, которая перехватывает сигнал SIGI NT, а перехватив этот сигнал, в пятый раз, завер- шает свое выполнение с помощью системного вызова exit. Программа, обеспечивающая выполнение требуемых для реакции на сигнал действий каждый раз заново устанавливает тип реакции на сигнал — перехват сигна- 188
ла. Это связано с тем, что при перехвате сигнала процессом тип реакции на сигнал переустанавливается на стандартный. Стандартным типом реакции на сигнал является, как Вы уже знаете, завершение выполнения процесса. Переустанавливать тип реакции на сигналы, игнорируемые Процессом, не требуется, что, впрочем, вполне очевидно. Попробуйте выполнить приведен- ную ниже программу и Вы увидите действие механизма сигналов на прак- тике: //include <signal.h> int nsigs; main(){ int onintr(); signal(SIGINT,&onintr); /«перехват сигнала SIGINT*/ for(;;) sleep(100); /* ждать 100 секунд */ onintr(sig) signal (SIGI NT,&onintr); printff'signal %d received\n",sig); if( + + nsigs > = 5) exit(); } Файл /usr/include/signal.h, включаемый в программу с помощью препроцес- сора, содержит все необходимые при работе с сигналами определения и объявления. Для получения более подробной информации об этом файле воспользуйтесь Руководством или приложением Д. Включив в программу файл <signal.h>, Вы можете при задании типа реакции на сигнал с помощью второго аргумента системного вызова signal использовать для игнорирова- ния сигнала SIG_IGN, а для переустановки типа реакции на сигнал в стан- дартный SIG_DFL. Возврат из функции, обеспечивающей требуемую реакцию на сигнал, в случае его перехвата осуществляется в ту точку программы, в которой ее выполнение было прервано из-за получения сигнала. Будьте осторожны, если в функции, обеспечивающей требуемую реакцию на перехватываемые сигналы, используется арифметика с плавающей точкой, поскольку в этом случае, если в основной программе также используется арифметика с пла- вающей точкой, могут возникать нежелательные побочные эффекты1. 1 Возникновение этих нежелательных побочных эффектов зависит от типа исполь- зуемой ЭВМ и реализации библиотечных функций. На ЭВМ семейства PDP-11 стандарт- ные функции для работы с сигналами не обеспечивают сохранения содержимого ре- гистров процессора с плавающей точкой перед переходом к обработке сигналов и их восстановления после завершения этой обработки. 189
8.8. ВВОД-ВЫВОД Наиболее часто в ОС UNIX используются те системные вызовы, кото- рые связаны с проведением операций ввода-вывода. В ОС UNIX ввод-вы- вод является крайне простой операцией и налагает на пользователя мини- мум ограничений. Прежде чем описать операции чтения из файла и записи в файл, необходимо рассмотреть операцию открытия файла, поскольку Вы не можете ни читать из закрытого файла, ни писать в закрытый файл. 8.8.1. системные вызовы open, close, creat В ОС UNIX Вы можете открыть файл тремя различными способами и, кроме того, воспользоваться одним трюком, который выглядит как откры- тие файла, но на самом деле таковым не является. Мы рассмотрим два способа открытия файла: с помощью системных вызовов open и creat. Системный вызов open используется для открытия файла только для чтения, только для записи и для чтения и записи. Системный вызов open может быть использован только для открытия уже существующего файла. В качестве своего значения системный вызов open возвращает целое, назы- ваемое пользовательским дескриптором файла. Пользовательскийдескрип- тор файла используется впоследствии для указания файла вместо имени файла. Системный вызов open имеет два аргумента: имя файла и режим открытия файла. Имя файла представляет собой строку символов. На са- мом деле аргументом является не сама строка символов, а указатель на эту строку символов. Режим открытия файла определяет возможность последующего доступа к файлу следующим образом: О — только чтение; 1 — только запись; 2 — чтение-запись. Следующий пример иллюстрирует открытие файла fred для чтения-записи: tildes = open("fred",2); При аварийном завершении выполнения системного вызова open возвра- щаемое им значение равно —1. Причиной аварийного завершения выполне- ния системного вызову open может служить то, что файл, который Вы пы- таетесь открыть, не существует, либо Вы не имеете разрешения на соот- ветствующий доступ к файлу, либо исчерпан лимит числа одновременно открытых в рамках процесса файлов1. В ОС UNIX каждый процесс может 1 На самом деле возможна еще одна причина аварийного завершения системного вызова open. Дело в том, что в ОС UNIX на этапе ее генерации фиксируется также суммарное число файлов, открытых одновременно во всех процессах, которое, как правило намного меньше произведения максимально возможного числа процессов на максимально возможное число одновременно открытых в рамках одного процесса файлов. Поэтому возможна такая ситуация, при которой суммарное число открытых файлов достигло предела, хотя в рамках процесса лимит числа одновременно откры- тых файлов еще не исчерпан. (Прим, ред.) 190
иметь одновременно открытым лишь определенное число файлов, фикси- руемое в процессе генерации ОС UNIX. Это число, как правило, не превы- шает 20, а чаще всего равно 16. По установленному соглашению пользова- тельские дескрипторы файлов 0,1, 2, зарезервированы следующим обра- зом: 0 — стандартный ввод; 1 — стандартный вывод; 2 — стандартный протокол. Если файл, который Вы хотите открыть, еще не существует, то с по- мощью системного вызова creat можно одновременно создать файл и отк- рыть его для записи. В качестве своего значения системный вызов creat так- же возвращает пользовательский дескриптор созданного и открытого в результате его выполнения файла. В случае аварийного завершения выпол- нения системного вызова creat возвращаемое им значение равно —1. Если Вы попытаетесь создать и открыть уже существующий файл с помощью системного вызова creat, то этот файл будет усечен так, что его размер станет равным 0 (т. е. файл станет пустым), а затем будет открыт для за- писи. При использовании системного вызова creat Вы должны указать код защиты создаваемого Вами файла. Следующий пример иллюстрирует соз- дание и открытие для записи файла fred, код защиты которого таков, что обеспечивает возможность доступа к этому файлу по чтению и по записи для всех без исключения категорий пользователей: tildes = creat("fred",0666); Естественно, что код защиты файла, создаваемого с помощью системного вызова creat, может быть модифицирован без Вашего ведома в соответст- вии со значением маски кода защиты файла. Если файл, который Вы хоти- те создать с помощью системного вызова creat, уже существует, а Вы не имеете разрешения на доступ к этому файлу по записи, происходит аварий- ное завершение выполнения системного вызова creat. Если лимит одновременно открытых в рамках процессов файлов ис- черпан, а Вам необходимо открыть еще один файл, то прежде чем открыть этот файл, следует закрыть один из уже открытых файлов с помощью сис- темного вызова close, аргументом которого является пользовательский дескриптор файла, который Вы хотите закрыть. Следующий пример иллюст- рирует закрытие файла, являющегося стандартным выводом: close(1); Напоминаем Вам, что при выполнении системного вызова exit закрываются все открытые в рамках процесса файлы. 191
8.8.2. системные вызовы read, write, Iseek Как Вы уже знаете, в ОС UNIX любой файл независимо от его содержи- мого представляет собой последовательность байт. Вы можете производить чтение или запись произвольного числа байт, при этом чтение и запись мо- гут быть осуществлены, начиная с произвольного места в файле. Чтение из файла после того, как встречен конец файла, невозможно, а запись, в файл после того, как встречен конец файла, возможна. Запись в файл после конца файла приводит к увеличению размера файла и может производится до тех пор, пока не будет исчерпано место диске. Файл может быть одновре- менно открыт в нескольких процессах, каждый из которых может осущест- влять операции чтения из этого файла и операции записи в этот файл1. В ОС UNIX доступ ко всем файлам осуществляется совершенно одинаково, вне зависимости от типа файла, более того, процессы даже не знают типа файла, с которым они имеют дело. Некоторые нелепые операции являются запрещенными. Примером такой нелепой операции может служить опера- ция чтения из специального файла, соответствующего АЦПУ. Системные вызовы read и write используются похожим образом. Для того чтобы воспользоваться любым из этих системных вызовов. Вы долж- ны в качестве его аргументов указать пользовательский дескриптор фай- ла, адрес в оперативной памяти и число байт, которое необходимо про- читать или записать. Ядро ОС UNIX пытается осуществить чтение или запись указанного числа байт, если это возможно. В качестве своего значения каж- дый из этих системных вызовов возвращает число реально прочитанных или записанных байт, которое может.отличаться от числа,указанного в ка- честве последнего аргумента любого из этих системных вызовов. Причиной такого отличия при выполнении операции записи может, например, яв- ляться ошибка устройства, а причиной такого отличия при выполнении операции чтения может, например, являться то, что.ещедо окончания чте- ния требуемого числа байт, встречен конец файла. Наиболее часто такое отличие возникает при проведении операций чтения из специальных фай- лов, соответствующих терминалам, поскольку при этом осуществляется чтение достаточно коротких строк, а указанное в качестве аргумента сис- темного вызова read число байт соответствует максимально возможной длине строки. Если значение, возвращаемое системным вызовом read, рав- но 0, то это означает, что ранее был достигнут конец файла. Такая ситуация может быть эмулирована на терминале нажатием клавиши, соответствую- щей специальному символу EOT, описанному в гл. 1. 1 Очевидно, в том случае, если запись в один и тот же файл осуществляется одновре- менно несколькими процессами, то могут возникнуть разного рода коллизии. В стан- дартной версии 7 ОС UNIX это разрешается, а в некоторых версиях операционных систем, подобных ОС UNIX, запрещается. Вы можете, выяснить допускается ли это в той версии ОС UNIX или подобной ей операционной системы, которую Вы используе- те, изучив имеющуюся у Вас документацию. 192
Каждый открытый файл имеет указатель чтения-записи. При чтении или записи п байт указатель чтения-записи передвигается на п байт вправо. В момент, непосредственно следующий за открытием файла, указатель чте- ния-записи всегда указывает на начало файла, т. е. на самый первый байт файла. Наличие указателя чтения-записи означает, что на практике к фай- лам обеспечивается последовательный доступ, поскольку любая операция чтения или записи осуществляется в соответствии с текущим положением указателя чтения-записи. Вы, наверное, заметили, что среди аргументов системных вызовов read и write нет аргумента, специфицирующего место- положение информации в файле. Указатель чтения-записи легко можно ус- тановить так, чтобы он указывал на требуемое место в файле, используя системный вызов Iseek. Наличие системного вызова Iseek позволяет осу- ществлять не только последовательный, но и произвольный доступ к фай- лам. Для того чтобы воспользоваться системным вызовом Iseek, следует в качестве его аргументов указать пользовательский дескриптор файла, ве- личину смещения указателя чтения-записи и базу смещения указателя чте- ния записи. Смещение указателя чтения-записи, которое может быть как положительной, так и отрицательной величиной, задает смещение в байтах. Если величина смещения указателя чтения-записи положительна, указатель чтения-записи смещается вправо от базы смещения указателя чтения-за- писи, если же величина смещения отрицательна, то указатель чтения-записи смещается влево от базы смещения указателя чтения-записи. Таким обра- зом база смещения указателя чтения-записи задает то место в файле, от которого будет осуществляться смещение указателя чтения-записи. База смещения указателя чтения-записи может быть задана одним из следую- щих трех способов: О — начало файла; 1 — текущее положение указателя чтения-записи; 2 — конец файла. В качествесвоегозначения системный вызов Iseek возвращает предыдущее значение указателя чтения-за писи. При попытке установить указатель чте- ния-записи левее начала файла значение, возвращаемое системным вызовом iseek равно —1. Ниже приведена программа на языке программирования Си, иллюстрирующая использование системного вызова Iseek: main(){ int fildes; long old,new; fildes = open ("fred”, 0); /* открыть файл fred для чтения */ if (fildes = = - 1){ perror ("fred"); exit(); } 7 Зак 1165 193
new = 5; old = lseek(fildes,new,O); /* найти 5-й байт файла */ printf("old pointer %D\n",old); } Как мы уже говорили, использование системных вызовов read и write крайне просто. Ниже приведена программа на языке программирования Си, иллюстрирующая использование системных вызовов read и write на примере копирования стандартного ввода на стандартный вывод, в кото- рой для временного хранения копируемой информации используется мас- сив buf: main(){ char buf[512]; int nread,ccount; ccount = 0; while((nread = read(0,buf,512)) != 0){ ccount + = nread; if(write(1 ,buf,nread) != nread){ perror("writing"); exit( -1); } } printf("%d characters read\n",ccount); } Сравните приведенную выше программу с программой, обеспечивающей копирование стандартного ввода на стандартный вывод средствами стан- дартной библиотеки ввода-вывода, приведенной в гл. 4. Попробуйте воспользоваться приведенной выше программой, а затем модифицируйте ее, воспользовавшись программой, иллюстрирующей использование сис- темного вызова Iseek так, чтобы полученная программа обеспечивала ко- пирование стандартного ввода на стандартный вывод, начиная не с 1-го, а с 5-го байта. Размер массива buf выбран равным 512 совсем не случайно. Дело в том, что в большинстве реализаций ОС UNIX наибольшая эффективность дос- тигается при проведении операций ввода-вывода порциями по 512 байт. Однако это не означает, что при проведении операций ввода-вывода Вы всегда должны пользоваться порциями информации именно такой длины. Можете осуществлять их более длинными или более короткими порциями, если Вам так удобнее. Мы бы не рекомендовали Вам выполнять операции ввода-вывода, связанные с передачей больших объемов информации, в ре- жиме "байт за байтом", поскольку такой режим довольно неэффективен. Еще один способ открытия файла — использование системного вызова pipe, а тем трюком, о котором мы упоминали, является использование 194
системного вызова dup. Системный вызов pipe обеспечивает создание канала для коммуникаций между процессами и в результате своего выполнения возвращает два пользовательских дескриптора для одного и того же файла — один для файла, открытого для чтения, а другой для файла, открытого для записи. Системный вызов dup обеспечивает создание копии уже имеющегося пользовательского дескриптора файла. Системный вызов dup не порождает для создаваемой им копии пользовательского дескриптора файла нового указателя чтения-записи. Для того чтобы иметь несколько указателей чтения- записи для одного и того же файла, необходимо соответствующее число раз воспользоваться системным вызовом open или системным вызовом creat. 8.8.3. СИСТЕМНЫЙ ВЫЗОВ ioctl Реализация системного вызова ioctl, используемого для установки различных параметров в программах, обеспечивающих взаимодействие с периферийными устройствами, зависит от используемой аппаратуры. Например, обычно при вводе пароля отключают режим вывода вводимых символов (режим "эхо"). Мы рекомендуем ознакомиться с особенностями реализации системного вызова ioctl для Вашей аппаратуры в имеющейся у Вас документации. 8.9. ЗАКЛЮЧЕНИЕ Итак, мы познакомили Вас с некоторыми из имеющихся в ОС UNIX системными вызовами. Мы надеемся, что Вы поняли, что собой представ- ляют системные вызовы, поскольку это совершенно необходимо для реше- ния различных задач. Понимание семантики системных вызовов позволит Вам максимально использовать возможности, предоставляемые ОС UNIX. Перечни системных вызовов, сигналов и номеров ошибок приведены в приложении Д. ГЛАВА 9. БИБЛИОТЕКИ ОС UNIX 9.1. ВВЕДЕНИЕ В отличие от таких языков программирования, как Фортран или Паскаль, язык программирования Си не имеет конструкций, обеспечивающих про- ведение операций ввода-вывода. Такие системные вызовы ОС UNIX, как open, read и write, вполне могут быть использованы для ввода и вывода информации программой, написанной на языке программирования Си, од- нако все они весьма примитивны по своей организации, а кроме того, яв- ляются машинно-зависимыми, т. е. реализация этих системных вызовов зависит от типа используемой ЭВМ. Например, если при написании програм- мы на языке программирования Си Вы руководствовались правилами размещения байт в слове, принятыми для ЭВМ семейства PDP-11, то при по- 7* 195
пытке перенести эту программу на ЭВМ семейства М6800, Вы непременно столкнетесь с ошибками ввода-вывода’. В этой главе мы рассмотрим имеющиеся в ОС UNIX стандартные библио- теки, в том числе и стандартную библиотеку ввода-вывода. Функции стан- дартной библиотеки ввода-вывода предоставляют пользователю ОС UNIX удобные стандартные средства для проведения операций ввода-вывода, а кроме того, их использование повышает мобильность программ. Послед- нее означает, что программа, использующая для проведения операций ввода-вывода только функции стандартной библиотеки ввода-вывода, может одинаково успешно функционировать на любой ЭВМ, работающей под управлением ОС UNIX1 2. Прежде чем мы с Вами начнем копаться в этом ящике Пандоры, посмотрим, как используются функции стандартных биб- лиотек. Такой подход к изучению стандартных библиотек кажется нам весьма удачным. 9.2. ПРЕПРОЦЕССОР Использование многих функций стандартных библиотек требует включе- ния в файл с исходным текстом Вашей программы содержимого некоторых стандартных файлов ОС UNIX. Так, исходный текст любой программы, написанной на языке программирования Си и использующей функции стан- дартной библиотеки ввода-вывода, в качестве одной из первых должен содержать строку #include <stdio.h> В таких случаях говорят, что "препроцессор включает файл stdio.h в ис- ходный текст программы". Файл /usr/include/stdio.h (а именно этот файл "включается препроцессором") содержит определения макроподстановок и переменных, используемых библиотечными функциями. Процедуры вклю- чения указанных таким образом файлов и выполнения макроподстановок, а также подготовка файла, содержащего исходный текст программы, к ус- ловной трансляции выполняется препроцессором на первой фазе трансля- ции. Делается это очень просто: препроцессор последовательно просматри- вает строки файла, содержащего исходный текст программы и, обнаружив в первой позиции очередной строки символ #, обрабатывает эту строку должным образом. 9.2.1. СИМВОЛЬНАЯ ЗАМЕНА И МАКРОПОДСТАНОВКА Если исходный текст программы содержит строку вида #defme identifier string 1 В ЭВМ семейства PDP-11 младший байт слова является четным, а в ЭВМ семейства М6800—нечетным. (Прим. 2 По-видимому, это сказано слишком сильно. [Прим. ред.) 196
то в результате обработки такой строки препроцессором каждый контекст identifier, содержащийся в любой строке исходного текста программы, начиная со следующей, будет заменен на контекст string. Аналогичным образом может быть определена макроподстановка. Допустим, что исход- ный текст программы содержит строку вида #define identifier(arg1,..., argn) string В таком случае после обработки этой строки препроцессором каждый кон- текст identifier, содержащийся в любой строке исходного текста програм- мы, начиная со следующей, будет заменен на контекст string, при этом все формальные параметры объекта identifier, т. е. параметры, перечислен- ные в списке параметров в определении макроподстановки (в нашем случае это arg1, arg2, . . ., argn), будут заменены на фактические параметры объек- та identifier, г. е. на те параметры, которые перечислены в списке парамет- ров объекта identifier при реальном использовании его в программе. Число формальных и фактических параметров объекта identifier должно быть одинаковым. Из этого правила есть, однако, одно исключение: если контекст, указан- ный в определении символьной замены или определении макроподстановки и заменяемый в обычной ситуации препроцессором на некоторый другой контекст, использован в исходном тексте программы как символьная кон- станта (т. е. заключен в двойные кавычки), то препроцессор не станет заме- нять его на другой контекст. Необходимо заметить, что это исключение не накладывает никаких ограничений на использование символьной замены или макроподстановки, так как, просматривая очередную строку исходного текста программы, препроцессор находит в ней и должным образом обраба- тывает (заменяет на другой контекст или нет) непременно все контексты, приведенные в определениях символьных замен или макроподстановок. Если определение символьной замены или макроподстановки слишком велико, чтобы его можно было поместить на одной строке исходного текста программы, то воспользуйтесь символом \ для переноса его на другую стро- ку исходного текста программы так, как это сделано в приведенном ниже примере: #define LONG—DEF Это очень длинное определение и\ поэтому оно должно быть размещено на нескольких последовательД ных строках. При подстановке такого определения могут воз-\ никнуть проблемы, но сейчас нам ничто не мешает использоД вать его для иллюстрации сказанного. Приведенное в примере "очень длинное определение" будет обработано препроцессором как одна строка. Процедура символьной замены наиболее полезна для определения символических констант, например: 197
^define BUFSiZE 400 //define EOF (-1) ^define NULL 0 char buffer[BUFSIZE], *bp, ch; bp = buffer; while ((ch = getcharf)) != EOF} { if (bp <= &buffer[BUFSIZE]) •bp+ + = ch; else { printff'buffer overflowed\n’'); Процедура же макроподстановки чаще используется для логических прове- рок, например: ^define isalpha(c) (о = 'a' 8t8t с< = 'z') char -аср; if (isalphaf *аср )) { Если фактические параметры объекта, указанного в определении макропод- становки, являются указателями, необходимо быть чрезвычайно осторож- ными и избегать использования операций уменьшения и увеличения. Рассмот- рим следующую конструкцию: if (isalpha (*аср++) ){• •.. После трансляции подобной конструкции Вы наверняка получите весьма странные результаты. 14, наконец, последнее. Обработка препроцессором строки вида #undef identifier вызовет отмену выполнения в дальнейшем всех символьных замен или мак- роподстановок, в которых был указан объект identifier. 9.2.2. УСЛОВНАЯ ТРАНСЛЯЦИЯ Строки исходного текста программы могут игнорироваться или трансли- роваться транслятором с языка программирования Си в зависимости от выполнения некоторого условия. Само условие может быть сформулировано одним из следующих способов: если исходный текст программы содержит строку вида #ifdef identifier 198
то условие считается выполненным, если объект identifier был указан к этому моменту в одном из определений символьных замен или макропод- становок; если исходный текст программы содержит строку вида #ifndef identifier то условие считается выполненным, если объект identifier не был указан к этому моменту ни в одном из определений символьных замен или макро- подстановок; если исходный текст программы содержит строку вида #if constant—expression то условие считается выполненным, если объект constant—expression имеет ненулевую величину (синтаксически в качестве объекта constant—expression может быть использовано любое арифметическое выражение, содержащее лишь константы). Итак, Вы видите, что условие игнорирования или трансляции строк исход- ного текста программы задается строкой некоторого специального вида. Если продолжить просмотр строк исходного текста программы, двигаясь при этом в прямом направлении, то среди строк исходного текста програм- мы могут оказаться строки следующего вида: #else и обязательно должна присутствовать строка вида #endif завершающая группу строк, подвергаемых условной трансляции. Если ус- ловие выполнено, то все строки исходного текста программы, заключенные между строкой вида #else (если такая строка присутствует в исходном тексте программы) и строкой вида #endif, игнорируются транслятором с языка программирования Си. Если же условие не выполнено, то трансля- тор с языка программирования Си игнорирует все строки исходного текста программы, заключенные между строкой, содержащей формулировку условия, т. е. строкой вида #Jfndef, либо строкой вида #ifdef, либо стро- кой вида #if constant—expression и строкой вида #else, либо, если строка вида #else в исходном тексте программы отсутствует, то строкой вида #endif. Выполнение условия можно добиться искусственно, воспользо- вавшись флагом —D команды сс. Например, команда сс —Dfred file.c вызовет трансляцию транслятором с языка программирования Си файла file.c и при этом условие, формулировка которого заключена в одной из строк файла, имеющей вид #ifdef fred будет выполнено. 199
ВНИМАНИЕ! Мы призываем Вас к экономному использованию конструк- ций условной трансляции, так как наличие большого числа этих конструк- ций в исходном тексте программы сильно затрудняет его чтение. 9.2.3. ВКЛЮЧЕНИЕ ФАЙЛОВ Если среди прочих строк исходного текста программы в файле содер- жится строка вида #include "filename" то в результате обработки содержимого такого файла препроцессором эта строка будет заменена на содержимое файла filename. При этом поиск файла с таким именем производится сначала в текущем каталоге, а в случае неудач- ного завершения поиска продолжается в нескольких каталогах, имеющих стандартные имена. В большинстве версий ОС UNIX таким стандартным ка- талогом является каталог /usr/include. Для того чтобы исключить поиск включаемого файла в текущем каталоге, необходимо модифицировать указанную строку следующим образом: #include <filename> После такой модификации поиск файла filename будет произведен лишь в каталогах, имеющих стандартные имена. Впрочем, при желании можно указать препроцессору на необходимость производить поиск включаемых файлов во вполне определенном каталоге, вместо каталогов со стандарт- ными именами; единственным ограничением при этом является ограничение на вид окончания имени включаемого файла: оно должно иметь вид .h. Проиллюстрируем сказанное с помощью следующей команды: сс —l/usr/andy/include file Как Вы, наверное, заметили здесь использован флаг —1 команды сс. Если теперь файл file содержит строку вида #include <defs.h> то последняя будет заменена препроцессором на содержимое файла /usr/ andy/include/defs.h. По принятому соглашению имена файлов, включаемых подобным образом, должны иметь окончания вида .h. Файлы, содержащиеся в каталоге /usr/include, содержат информацию, описывающую саму ОС UNIX, например структуру дписателей файлов ОС UNIX. Конечно, Вы може- те использовать механизм включения файлов для создания собственных файлов макроподстановок и/или символьных замен, но нам кажется более разумным использовать эту возможность для размещения некоторой про- граммы на языке программирования Си в нескольких файлах. В некоторых случаях при использовании такого подхода листинги больших программ становятся крайне неуклюжими. В такой ситуации может оказаться более разумным размещать во включаемых файлах лишь те функции про- граммы, которые тесно связаны между собой, например функции, обеспе- 200
чивающие работу с таблицей символов. Поскольку порядок следования функций, из которых состоит 'программа на языке программирования Си, произволен, то исходный текст программы может быть размещен в нескольких отдельных файлах и без использования предоставляемой препро- цессором возможности включения файлов, учитывая при этом, что любая функция должна быть целиком размещена в одном и том же файле. Заме- тим, что имя каждого из таких файлов должно иметь окончание вида .с. Теперь предположим, что исходный текст программы на языке програм- мирования Си размещен в файлах m.iin.c, passl.c и pass2.c. В таком случае ввод следующей команды: сс main.c passl.c pass2.c приведет к раздельной трансляции содержимого всех трех файлов и поме- щению полученного в результате объектного кода в файлы main.о pass'!.о и pass2.o соответственно. После этого из объектного кода, содержащегося в этих трех файлах, будет скомпонована выполняемая программа и поме- щена в файл a.out. Итак, в результате получена выполняемая программа, исходный текст которой был размещен в трех отдельных файлах. Теперь если возникнет необходимость ретрансляции только одного из них, напри- мер файла passl.c, то вводе терминала команды сс passl.c ma in.о pass2.o приведет к трансляции содержимого файла passl.c, помещению полученного в результате объектного кода в файл passl.o, компоновке выполняемой программы из объектного кода, содержащегося в файлах passl.o, main.o и pass2.o, помещению полученной в результате выполняемой программы в файл a.out. 'Соглашение об окончаниях имен файлов, содержащих исход- ный текст программ на языке программирования Си, принято с той целью, чтобы транслятор с языка программирования Си мог отличить файл, содер- жащий объектный код программы, от файла, содержащего исходный текст программы. Если в Ваши планы не входит получение выполняемой программы, може- те воспользоваться флагом —с команды сс, например сс —с pass2.c Ввод с терминала этой команды приведет к трансляции содержимого файла pass2.c и в случае успешного завершения трансляции — к помещению полу- ченного в результате объектного кода в файл pass2.o. Все глобальные переменные, используемые функциями, исходные тексты которых размещены в отдельных файлах, должны быть объявлены в каж- дом из этих файлов. Для упрощения модификации исходного текста про- граммы, а также для экономии бумаги полезно разместить все подобные объявления глобальных переменных в одном файле, например в файле global.h. В таком случае каждый из файлов,, содержащих исходный текст программы на языке программирования Си, должен содержать строку вида #include "global.h" 201
9.3. КОМПОНОВЩИК Хотя Вам и удалось получить файл, содержащий выполняемую програм- му, скомпонованную из нескольких файлов, содержащих объектный код, с помощью только команды сс, в действительности компоновка файлов, содержащих объектный код была выполнена командой Id или компонов- щиком. Команда Id объединяет несколько файлов, содержащих объектный код, в один файл, содержащий выполняемую программу, удовлетворяя при этом все внешние ссылки и отыскивая в стандартных библиотеках ОС UNIX функции, которые хотя и не были специально объявлены в файлах, содержащих исходный текст программы, но ссылки на которые имеются в файлах, содержащих объектный код. Если не указано противное, то ре- зультат работы команды Id помещается в файл a.out. Результат работы команды Id будет представлять собой выполняемую программу только в том случае, если трансляция исходного текста этой программы завер- шилась успешно и на этапе компоновки не было обнаружено неудовлет- воренных внешних ссылок. Режим выполнения команды Id может быть специфицирован с помощью флагов. Число этих флагов весьма велико, и мы не станем здесь останавливаться на них, тем более что команда сс, вызываю- щая на выполнение команду Id, достаточно корректно готовит список аргу- ментов последней, так что нет необходимости заботиться о них до тех пор, пока Вам не придет в голову проделать что-либо неординарное. Один из флагов команды Id мы все-таки рассмотрим; речь идет о флаге — 1х, здесь х представляет собой строку символов, специфицирующую необ- ходимую стандартную библиотеку. В результате использования флага — 1х поиск функций, не определяемых в программе, будет начат в файле /lib/libx.a и в случае неудачи продолжен в файле /usr/lib/libx.a. Флаг — 1х может быть использован как флаг команды сс, в этом случае он будет пере- дан командой сс команде Id тоже в качестве флага. Так, использование команды сс с флагом — 1с приведет к поиску командой Id функций, не оп- ределенных в программе, в файле /lib/libc.a, содержащем, как известно, стандартную библиотеку, включающую все наиболее часто используемые библиотечные функции. В предыдущей главе мы уже рассмотрели системные вызовы ОС UNIX, и хотя все приведенные в ней примеры представляют собой фрагменты программ на языке программирования Си, тем не менее использование библиотечных функций в программах, например на языке программиро- вания Фортран 77, аналогично и отличается лишь тем, что в языке програм- мирования Фортран 77 для обращения к подпрограммам используется оператор CALL. Теперь мы рассмотрим некоторые другие библиотечные функции, включенные в стандартную библиотеку, которая находится в файле /lib/libc.a. Мы даже не пытались привести здесь полное описание всех этих библиотечных функций, за этим Вам лучше обратиться к разд. 3 Руководства. Тем не менее некоторые из них, особенно функции printf и scanf, вполне заслуживают детального рассмотрения их в нашей книге. В 202
Руководстве библиотечные функции перечислены в алфавитном порядке, мы же разобьем их на четыре основные группы: библиотечные функции, входящие в состав стандартной библиотеки ввода-вывода; библиотечные функции для решения различных математических задач; библиотечные функции смешанного назначения; библиотечные функции специального назначения. 9.4. БИБЛИОТЕЧНЫЕ ФУНКЦИИ, ВХОДЯЩИЕ В СОСТАВ СТАНДАРТНОЙ БИБЛИО- ТЕКИ ВВОДА-ВЫВОДА Каждый "источник" или "приемник" информации рассматривается функ- циями, входящими в состав библиотеки ввода-вывода, как поток данных. Все процессы, порожденные процессом, в рамках которого выполняется интерпретатор команд shell, автоматически наследуют три потока данных: стандартный ввод (std in), стандартный вывод (stdout) и стандартный протокол (stderr)* 1. Для ссылок на поток данных функции, входящие в состав стандартной библиотеки ввода-вывода, используют указатели на объект некоторого предварительно объявленного типа — FILE2. Это и мно- гие другие определения и объявления подготовлены для Вас в файле /изг/ /include/stdio.h, содержимое которого должно быть включено в исход- ный текст любой программы, использующей входящие в состав стандарт- ной библиотеки ввода-вывода функции. Кратко рассмотрим стандартную библиотеку ввода-вывода, для чего опишем некоторые из входящих в нее функций (описание всех функций, входящих в стандартную библиотеку ввода-вывода, дано в приложении Г). Перед осуществлением любых операций ввода-вывода с некоторым фай- лом последний должен быть открыт как поток данных, например с помощью библиотечной функции fopen, которая возвращает в качестве своего зна- чения указатель на файл. Первый аргумент библиотечной функции fopen оп- ределяет имя открываемого как поток данных файла, а второй — тип досту- па к открываемому файлу следующим образом: если второй аргумент — символ г, то файл, имя которого указано в ка- честве первого аргумента, будет открыт как поток данных лишь для ввода информации из него; если второй аргумент — символ w, то файл, имя которого указана в ка- честве первого аргумента, будет в случае необходимости создан и открыт как поток данных для вывода информации в него; * Если следовать Руководству, то поток данных следует определить как совокуп- ность некоторого открытого файла и механизма буферизации операций ввода-вывода из/в него. При атом необходимо иметь в виду, что функции, входящие в состав стан- дартной библиотеки ввода-вывода, обеспечивают еще один дополнительный уровень буферизации, о котором и идет речь в предыдущем предложении. {Прим. ред.} 1 Это справедливо недля всех версий ОС UNIX. {Прим, ред.} 203
если второй аргумент — символ а, то файл, имя которого указано в ка- честве первого аргумента, будет открыт как поток данных для вывода информации в него и выводимая информация будет добавлена после исход- ного содержимого файла. Таким образом, открыть как поток данных файл fred для ввода можно следующим образом: FILE *fp; fp = fopen ("fred", "r"); Заметим, что FILE — это просто тип данных, такой, как, например, int или char. Если по какой-либо причине операция открытия файла как потока данных завершилась неудачей, например Вы пытались открыть для ввода несуществующий файл, то значением, возвращаемым библиотечной функ- цией fopen, будет пустая строка. Разобраться в подобной ситуации Вам поможет следующий фрагмент программы на языке программирования Си: if ((fp = fopen ("fred", "r")) ==NULL) { printf("cannot find fred\n'’); exit(); } Если Вам все-таки удалось открыть файл как поток данных, то вполне естественно, что у Вас возникнет желание научиться осуществлять опера- ции ввода-вывода с этим файлом. Для этого можно воспользоваться биб- лиотечными функциями getc и putc. Функция getc возвращает в качестве своего значения следующий символ из потока данных, причем ссылка на этот поток данных осуществляется с помощью указателя на объект типа FILE. При достижении конца потока данных (файла) библиотечная функция getc возвращает в качестве своего значения EOF. Следующий пример иллю- стрирует сказанное: int ch; FILE *fp; while ((ch = getc (fp)) ! = EOF) { } Необходимо заметить, что на самом деле значение, возвращаемое библио- течной функцией getc, всегда имеет тип int, поскольку это необходимо для обеспечения совместимости с другими функциями, входящими в состав стандартной библиотеки ввода-вывода. Действия, выполняемые библиотечной функцией putc, прямо противопо- ложны действиям, выполняемым библиотечной функцией getc. В результате выполнения функции putc символ выводится в поток данных, который был 204
предварительно открыт, например, с помощью библиотечной функции fopen. Если в ходе операции вывода символа в поток данных возникла ошибка, то в качестве своего значения библиотечная функция putc возвратит EOF. Следующий пример иллюстрирует использование библиотечной функции putc: char ch; FILE -fp; if (putcfch, fp) = = EOF) printf("Failed to put a character^"), Если Вам необходимо осуществлять операции ввода-вывода слов с некото- рым потоком данных, то воспользуйтесь библиотечными функциями getw и putw соответственно; с точки зрения использования они совершенно ана- логичны библиотечным функциям getc и putc. Для проведения операций ввода-вывода с терминалам в стандартной библиотеке ввода-вывода имеются две специальные библиотечные функ- ции: getchar и putchar, с помощью которых Вы можете соответственно ввести один символ с терминала или вывести один символ на терминал. Эти библиотечные функции используют стандартные указатели на объект типа FILE: stdin и stdout, что в обычной ситуации приводит к вводу-выводу информации на терминал; для переназначения ввода-вывода необходимо воспользоваться системным вызовом dup (см. dup (2)). Библиотечные функции getchar и putchar определены в файле Zusr/include/stdio.h сле- дующим образом: ^define getchar() getc(stdin) ^define putchar(c) putc(c, stdout) т. e. обращение к ним приводит в итоге к обращению к библиотечным функ- циям getc и putc. Здесь резонно задать вопрос: "Как же осуществить вывод на терминал строки символов, ведь библиотечная функция putc производит вывод на терминал лишь одного символа за одно обращение?" Для решения подобных задач в составе стандартной библиотеки ввода-вывода имеется еще одна библиотечная функция — fpues. Библиотечная функция fputs осу- ществляет последовательный вывод символов некоторой строки, являющей- ся ее первым аргументом, в файл, предварительно открытый как поток данных и определяемый ее вторым аргументом. Например: FILE -fp; fputs("helIo sailor", fp); Действия библиотечной функции puts совершенно аналогичны действиям библиотечной функции fputs, с той лишь разницей, что библиотечная функ- ция puts осуществляет вывод всегда на стандартный вывод, т. е. на терминал, и, кроме того, после вывода последнего символа строки выводит на терми- нал символ newline. Например, использование оператора puts ("hello sailor"); 205
приведет к выводу на терминал следующего: hello sailor Кроме библиотечных функций, осуществляющих просто ввод-вывод отдельных символов, целых чисел или строк символов в составе стандартной библиотеки ввода-вывода имеются библиотечные функции для реализации форматного ввода-вывода. Например, библиотечные функции printf и scanf реализуют форматный вывод и форматный ввод соответственно и в совокуп- ности представляют собой весьма мощное средство. Одной из замечательных особенностей этих библиотечных функций является их способность осуще- ствлять преобразование из двоичного представления в символьное и наобо- рот. Фактически мощность этого средства так велика, что большинство поль- зователей не в силах распорядиться ею. Единственная возможность овладеть использованием этих функций — это как можно чаще пользоваться ими на практике. Поэтому мы настоятельно рекомендуем Вам для начала воспро- извести все приводимые нами примеры, а потом, используя их, попытаться написать какую-нибудь программу самостоятельно. Библиотечная функция printf производит преобразование, формати- рование и вывод на стандартный вывод своих аргументов под управ- лением форматной строки. Библиотечные функции fprintf и sprintf произ- водят вывод результатов своей работы в специфицированный указателем поток данных и строку символов (массив типа char) соответственно. Кстати, следить за переполнением строки символов, в которую библиотеч- ная функция sprintf производит вывод, Вам придется самостоятельно. В качестве примера рассмотрим следующий фрагмент программы на языке программирования Си: char ‘control, »s; FILE 4р; printf(control, arg, ...); fprintf(fp, cohtrol, arg, ...); sprintf(s, control, arg, ...); Форматная строка control, указатель на которую описан в первой строке приведенного фрагмента программы, может содержать объекты двух типов: просто символы и спецификации преобразования и формата. Объекты пер- вого типа не подвергаются никаким преобразованиям и просто копируются, объекты же второго типа, или спецификации преобразования и формата, используются соответствующей библиотечной функцией для проверки корректности задания аргументов этой библиотечной функции и преобра- зования их требуемым образом. Спецификация преобразования и формата представляет собой последовательность двух символов, первым из которых должен обязательно быть символ %, а второй символ специфицирует соб- ственно преобразование. Ниже перечислены виды этих символов и соот- ветствующие им типы преобразования: 206
d — десятичное x - шестнадцатеричное е, f, g — с плавающей точкой s — строка символов о — восьмеричное и — беззнаковое целое с — символьное % — вывод символа % Например, приведенный ниже фрагмент программы на языке программи- рования Си может быть использован для вывода на терминал одной и той же численной величины в трех различных форматах: в виде десятичного числа, в виде восьмеричного числа и в виде шестнадцатеричного числа: int а; а = 10; printf("%d - %о - %х\п”, а, а, а); 6 результате выполнения приведенного выше фрагмента программы на языке программирования Си на терминал будет выведено следующее: 10- 12 -а Не забудьте, что содержимое форматной строки, не считая спецификаций преобразования и формата, выводится на терминал без каких-либо измене- ний. Приведенный ниже фрагмент программы на языке программирования Си иллюстрирует использование библиотечной функции printf для вывода на терминал числа с плавающей точкой в трех различных форматах: float х; х = 560.0 printf("%f - %е - %g\n", х, х, х); В результате выполнения этого фрагмента программы на языке программи- рования Си на терминал будет выведено следующее: 560.000000 - Б.бОООООе + 02 - 560 В результате выполнения следующего фрагмента программы на языке программирования Си, разобраться в котором Вам предстоит самостоя- тельно: char «аср.’Ьор, ch; аср= "Example of a string printed using %s\n"; bcp = "The ascii code for character '%c' is %d\n"; ch = 'a'; printf("%s", acp); printffbcp, ch, ch); на терминал будет выведено следующее: Example of a string printed using %s The ascii code for character 'a' is 97 207
Не так давно мы утверждали, что спецификация преобразования и формата представляет собой совокупность двух символов. На самом деле между этими двумя символами может быть помещена строка символов. Если указанная строка содержит лишь цифры, то ее содержимое можно рассмат- ривать как целое положительное число, которое в таком случае определяет минимальное число знаков в выведенном после соответствующего преоб- разования аргументе библиотечной функции printf. Если первым символом указанной строки является символ —, а остальными цифры, то выведенный после соответствующего преобразования аргумент будет выровнен по лево- му краю поля формата. Строка цифр, перед которыми стоит символ ., указы- вает библиотечной функции printf необходимую точность представления числовой величины с плавающей точкой или максимальное число знаков в выводимой строке символов. Например, в результате выполнения следую- щего фрагмента программы на языке программирования Си int a; float х; а = 12; х = 560.0; printf("%2d - %-4d - %6d\n", а, а, а); printf("%.0f - %.2f - %2.2f\n", x, x, x); на терминал будет выведено следующее: 12-12- 12 560 - 560.00 - 560.00 Обратите внимание на то, что в последней спецификации преобразования и формата для величины с плавающей точкой указание о минимальном числе знаков в формате выведенного аргумента не выполнено. Это связано с тем, что неразумные указания на слишком малое число знаков в формате выво- димого аргумента библиотечной функции printf игнорируются последней. Если Вы специфицировали точность представления величины с плавающей точкой меньшую, чем минимальное число знаков в формате выводимого аргумента, то при выводе указанная величина будет округлена. Так, в ре- зультате выполнения следующего фрагмента программы на языке програм- мирования Си: float х; х = 42.647; printf("х = %.2f\n", х); на терминал будет выведено следующее: х = 42.65 Полный набор спецификаций преобразования и формата приведен в при- ложении Г. Применение многих из них настолько очевидно, что Вам вполне должно хватить объяснений, приведенных в этом приложении. Единственным исключением является, пожалуй, указание точности пред- 208
ставления числовой величины при выводе строки символов. Мы приведем несколько вариантов спецификаций преобразования для форматного вы- вода строки символов, а Вы попробуйте разобраться в этом вопросе самостоятельно. В качестве аргумента библиотечной функции printf вос- пользуемся строкой hello sailor, а поле формата выведенной на терминал строки выделим с помощью символов Итак, %s %5s %-7s %20s %-20s %10.5s %-10.5s %. 10s L_ihello i_isailor: i_r hello i—i sailor: i_ihello i_jsailor: l_ii_ii_iLJL_ii_iL_n_jhello sailor: i_.hello sailor: hello: i—i hello l_jhello i_isail: ВНИМАНИЕ! Библиотечные функции printf, fprintf и sprintf используют форматную строку для определения числа аргументов, подлежащих выводу на термина?, а также для определения типов переменных, использованных в качестве аргументов. Несоответствие числа спецификаций преобразования и формата числу подлежащих выводу аргументов или же несоответствие типов аргументов типам специфицированных преобразований, приведет к очень печальным последствиям. В частности, при отсутствии достаточного числа подлежащих выводу аргументов или при несовпадении типов аргумен- тов и типов преобразований выполнение программы будет прервано, как ес- ли бы произошел сбой в работе ЭВМ или сбой при осуществлении обмена ин- формацией с диском. Действия, производимые библиотечной функцией scanf, прямо противопо- ложны действиям, производимым библиотечной функцией printf. При этом библиотечная функция scanf обладает теми же способностями преобразова- ния входной информации, также используя для этого спецификации пре- образования и формата, но вместо вывода на терминал осуществляет ввод с терминала. Кроме библиотечной функции scanf в составе стандартной би- блиотеки имеются библиотечные функции fscanf и sscanf. Общий принцип действия всех этих библиотечных функций таков: информация вводится из некоторого источника информации, проверяется на соответствие содер- жимому форматной строки, а затем форматируется и выводится в предназ- наченные для ввода аргументы функций. При этом для ссылки на аргумен- ты, предназначенные для ввода, используются указатели на переменные со- ответствующего типа. Библиотечная функция scanf осуществляет ввод ин- формации с терминала, библиотечная функция fscanf осуществляет ввод информации из потока данных, специфицированного указателем на объект типа FILE, а библиотечная функция sscanf осуществляет ввод информации из строки символов (массива типа char), специфицированной указателем на 209
нее. Проиллюстрируем сказанное следующим фрагментом программы на языке программирования Си: char *control,-s; FILE -fp; scanffcontrol, arg, fscanf(fp, control, arg, ...); sscanf(s, control, arg, ...); Форматная строка control может содержать объекты нескольких типов. В простейшем случае она может содержать произвольные символы, кроме символа %; в этом случае каждый очередной вводимый символ должен в точности совпадать с очередным символом форматной строки. Из этого пра- вила есть, однако, одно исключение — символы, являющиеся разделителями. К ним относятся такие символы, как ш, tab, newline. Для указания спецификаций преобразования и формата здесь вновь ис- пользуется символ %. Собственно преобразование вновь специфицируется одним сим волом, а между символом % и символом, специфицирующим собст- венно преобразование, может быть помещено целое число, специфицирующее максимальное число знаков во введенном после преобразования аргументе (или ширину поля формата) и/или символ «. Ниже перечислены виды сим- волов, специфицирующих собственно преобразования и соответствующие им типы преобразования: d — десятичное х — шестнадцатеричное с — символьное f, е, — с плавающей точкой о — восьмеричное h — короткое целое s — строка символов % — ввод символа % Использование указанных спецификаций преобразования и формата приво- дит к тому,что описываемые библиотечные функции осуществляют ввод из некоторого источника информации (каждая из своего) до тех пор, пока все требования, предъявляемые к вводимой информации со стороны библиотеч- ной функции и основанные на содержимом форматной строки, не будут удовлетворены. Операция преобразования и формата обрабатывает символ, следующий непосредственно за символом, преобразованным и введенным с помощью операции, соответствующей предыдущей спецификации преобра- зования и формата. Символы, вводимые описываемыми библиотечными функциями в соответствии с некоторой конкретной спецификацией преоб- разования, и формата,заполняют поле формата, затем подвергаются преоб- разованию и выводятся в место, на которое указывает соответствующий аргумент функции. Если же в спецификации преобразования присутствует символ * , то заполненное поле формата просто игнорируется. Итак, поля формата представляют собой строки символов, не содержа- щие символов, которые являются разделителями, и заполняются вводи- мыми символами до момента ввода символа, являющегося разделителем, или достижения указанной в спецификации преобразования и формата ши- 210
рины поля формата. Однако хватит теории, перейдем к практике. Введите с терминала следующую программу на языке программирования Си: main() { int a; char ch; float x, v; scanf("%2d %»d %f %1s %f", &a, &x, &ch, &v); printf("a = %d\n", a); printf("x = %f\n", x); printf("ch = %c\n", ch); printf("v = %e\n", v); передайте исходный текст этой программы на трансляцию транслятору с языка программирования Си и затем вызовите на выполнение полученную программу, а в качестве входной информации введите с терминала следую- щую строку символов: 4267 42.67 Т 53. 806е-12 Мы надеемся, что в результате на терминал будет выведено примерно следую- щее: а = 42 х = 42.669998 ch = Т v = 5.3806e-11 Первая из указанных в форматной строке спецификаций преобразования и формата — %2d указывает библиотечной функции scanf на необходимость интерпретации двух первых введенных символов как целых десятичных чи- сел и помещения результата в переменную а. Два следующих символа — 67 игнорируются функцией scanf, так как им соответствует спецификация пре- образования и формата % * , а первый же из следующих за символами 67 символ 1-1 прекращает операцию ввода и преобразования в соответствии с этой спецификацией преобразования и формата. Сами символы uj игнори- руются функцией scanf, поэтому следующим будет введен символ 4, а сле- дующей используемой для ввода и преобразования спецификацией преобра- зования и формата станет спецификация преобразования и формата %d, указывающая библиотечной функции scanf на необходимость интерпрета- ции последовательности символов 42.67 как десятичного числа с плаваю- щей точкой и помещения результата в переменную х. Конечно, десятичное число с плавающей точкой будет введено лишь как аппроксимация истин- ного значения, поэтому и на терминале Вы увидите 42.669998. Следующая спецификация преобразования и формата должна показаться Вам чрезвычайно странной. У Вас может возникнуть вопрос: "А почему не просто %с?". Дело в том, что спецификацию преобразования %е целесообраз- но использовать в специальных случаях, так как при осуществлении ввода и 211
преобразования в соответствии с этой спецификацией преобразования отме- няется игнорирование символов, являющихся разделителями. В нашем слу- чае эта особенность использования спецификации преобразования и формата %с приведет к вводу в переменную ch символа ш, в то время как нам бы хотелось ввести символ Т. Именно поэтому мы и воспользовались специфи- кацией преобразования и формата %1s,T.e. спецификацией преобразования и формата "односимвольной строки". При необходимости осуществить ввод нескольких символов, среди которых отсутствуют разделители, используй- те спецификацию преобразования и формата %ns, где п — целое десятичное число, равное числу символов, которое Вы хотите ввести. Заканчивая описа- ние приведенного выше примера, обсудим вкратце использование последней спецификации преобразования и формата — %f. Эта спецификация преобра- зования и формата является синонимом спецификации преобразования и формата %е, которая указывает библиотечной функции scanf на необходи- мость интерпретации последовательности символов 53.806е-12 как числа с плавающей точкой и помещения результата преобразования в переменную v. Теперь попробуйте воспроизвести следующую программу: main() { int a; float х; char ch[3], wd[20]; char »s = "X-values are 1, 2.7 and 14.6\n"; sscanf(s, "%s %3s %d, %*f and %f", wd, ch, &a, &x); printf("word = %s\n", wd); printf("ch = %c %c %c\n", ch [0],ch [1],ch [2]); printf("a = %d\n",a); printf("x = %f\n",x); } После трансляции исходного текста этой программы и выполнения получен- ной выполняемой программы на терминал должно быть выведено следую- щее: word = X-values ch = аге а = 1 х = 14.600000 Можете Вы объяснить, почему это так? Следующая спецификация преобразования и формата может быть исполь- зована лишь для ввода из строки символов с помощью библиотечной функ- ции sscanf. Речь идет о спецификации преобразования и формата % [...]. Использование этой спецификации преобразования и формата указывает библиотечной функции sscanf на изменение вида символа, ввод которого завершает операцию ввода в соответствии с этой спецификацией преобразо- вания и формата (до сих пор такими символами, как Вы помните, были 212
символы, являющиеся разделителями). Вслед за символом [ в специфика- ции преобразования и формата %[...] должна быть у казана строка символов. При осуществлении операции ввода в соответствии с такой спецификацией преобразования и формата последняя будет завершена, как только библи- отечная функция sscanf введет символ, не совпадающий ни с одним из сим- волов строки, указанной в спецификации преобразования и формата. Если же первым символом строки, указанной в спецификации преобразования и формата, является символ Л, то операция ввода, осуществляемая в соот- ветствии с такой спецификацией преобразования и формата, будет заверше- на, как только библиотечная функция sscanf введет символ, совпадающий с одним из символов строки, указанной в спецификации преобразования и формата. С помощью подобной спецификации преобразования и формата Вы можете указывать собственный набор символов, завершающих операцию ввода. В качестве иллюстрации сказанного рассмотрим следующую програм- му на языке программирования Си: main() { float х; char word[20]; char «string = "The value of x = 563.703\n"; sscanf(string, "%[ л = ] = %.ЗЛп", word, &x); printf ("word = ’%s'\n", word); printf ("x = %f\n",x); } в результате трансляции исходного текста этой программы на языке про- граммирования Си и последующего выполнения полученной выполняемой программы на терминал будет выведено следующее: word = "The value of x" x = 563.703 При использовании спецификации преобразования и формата % [Л. . .] сам символ, ввод которого вызывает завершение операции ввода, не копируется из строки символов, специфицированной указателем, являющимся первым аргументом библиотечной функции sscanf. В предыдущем примере символ = не будет скопирован в строку символов string, и поэтому при необходи- мости его следует ввести отдельно. Этот факт и вызвал появление второго символа = . Попробуйте удалить этот второй символ = и посмотрите, что получится. А теперь объясните эффект. Рассмотрим еще один пример — "программу извлечения части строки" — иллюстрирующий предыдущее замечание. Эта программа может быть исполь- зована для извлечения из некоторой строки символоводной или нескольких последовательностей символов, разделенных с помощью символов , . Может быть, приводимый фрагмент программы покажется Вам слишком сложным, но, поверьте нам, понимание принципов его работы очень пригодится. 213
Итак, введите с терминала следующий фрагмент программы на языке программирования Си: char string 1 [20] ,string2 [20] ,string3[20] ,string4[20]; scanf("% [A,] ,%[Ax,],% [A,], % [A,] ", stringl, string2, string3, string4); а после трансляции и вызова на выполнение полученной выполняемой про- граммы введите с терминала строку символов it can be seen, therefore, that if, under certain conditions,. В результате выполнения приведенного выше фрагмента программы на язы- ке программирования Си значения строк будут следующими: stringl = "it can be seen" string? = "therefore" string3 = "that if' string4 = "under certain conditions" ВНИМАНИЕ! Аргументы библиотечной функции scanf, в которые произ- водится ввод, должны быть указателями на переменные соответствующего типа. Кроме того. Вам необходимо позаботиться о том, чтобы вводимые с помощью библиотечной функции scanf символы "нашли себе пристанище в указанных аргументах". Одна из наиболее распространенных ошибок ил- люстрируется следующим фрагментом программы на языке программиро- вания Си: float х; scanf("%f", х); в то время как при корректном использовании библиотечной функции scanf этот фрагмент выглядел бы следующим образом: float х; scanf("%f",&x); Подобные ошибки приводят, как правило, к аварийному завершению вы- полнения программы. После завершения всех операций ввода-вывода файлы, открытые как по- токи данных должны быть закрыты. Для этого обычно используется библи- отечная функция fclose. Заметим, что в действительности закрывать файлы, открытые как потоки данных для ввода, нет необходимости, ибо все они будут закрыты автоматически по завершении всех процессов, соответствую- щих Вашей программе. Тем не менее проиллюстрируем процедуру закрытия некоторого файла, открытого как поток данных: FILE »fp; fclose(fp); Однако файлы, открытые как потоки данных для вывода, — это совсем дру- гое дело. Использование механизма буферизации ввода-вывода библиотеч- 214
ными функциями, входящими в состав стандартной библиотеки ввода-вы- вода, приводит к тому, что к моменту завершения всех процессов, соот- ветствующих Вашей программе, некоторая часть инфоргэации не будет выведена в соответствующие файлы. В таком случае "тупое" использование библиотечной функции fclose приведет к тому, что эта часть информации бу- дет навсегда потеряна1. Во избежание этого необходимо перед закрытием файла, открытого как поток данных для вывода (и при автоматическом закрытии, и при использовании библиотечной функции fclose), воспользо- ваться библиотечной функцией fflush, выполнение которой приведет к при- нудительному выводу всей информации в соответствующий этому потоку данных файл. Проиллюстрируем это с помощью следующего фрагмента программы на языке программирования Си; FILE -fp; fflush(fp); fclose(fp); В некоторых версиях ОС UNIX библиотечные функции, входящие в состав стандартной библиотеки ввода-вывода, используют строчно-ориентирован- ный механизм буферизации операций вывода информации на терминал. Это спасает Вас от потери информации до тех пор, пока перед Вами не встанет задача вывода на терминал части строки символов (например, промптера), так как фактический вывод на терминал будет произведен лишь после по- явления в выводимой строке символов символа linefeed. Однако и это пре- пятствие можно преодолеть, если воспользоваться библиотечной функцией fflush сразу после использования библиотечных функций print или putc, что иллюстрирует следующий фрагмент программы на языке программирова- ния Си: printff'- >"); fflush (stdout); 9.5. БИБЛИОТЕЧНЫЕ ФУНКЦИИ ДЛЯ РЕШЕНИЯ РАЗЛИЧНЫХ МАТЕМАТИЧЕСКИХ ЗАДАЧ Библиотечные функции, входящие в состав библиотеки, содержащиеся в файле /lib/libm.a, автоматически используются транслятором с языка про- граммирования Фортран. Чтобы воспользоваться этими библиотечными функциями в программе на языке программирования Си, необходимо при- менить команду сс с флагом —Im. В исходный текст любой программы на языке программирования Си, использующей указанные библиотечные функ- ции, должен быть включен файл /usr/ include/math.h, т. е. файл, содержащий 1В некоторых версиях ОС UNIX этого не происходит. (Прим, ред.} 215
исходный текст программы на языке программирования Си для этого в качестве одной из первых должен содержать строку вида ^include <math.h> В приложении Г весьма подробно описаны все библиотечные функции для решения математических задач. Сюда входят библиотечные функции, реализующие вычисление значений функции Бесселя, тригонометрических и гиперболических функций. В случае возникновения ошибок в ходе их вы- полнения библиотечные функции, входящие в состав этой библиотеки, устанавливают в качестве значения переменной еггпо причину ошибки; в данном случае переменная еггпо может принимать еще два специальных значения. Такими значениями являются: EDOM — аргумент функции выходит за границу области определения функции. Например, аргумент библиотечной функции asin или библиотеч- ной функции acos больше 1. ERANGE — значение функции непредставимо в данной ЭВМ. Например, значение библиотечной функции tan в 0. 9.6. БИБЛИОТЕЧНЫЕ ФУНКЦИИ СМЕШАННОГО НАЗНАЧЕНИЯ Библиотечные функции, отнесенные к этой группе, составляют основную часть библиотеки, содержащейся в файле/lib/libc.a. Команда Id автоматичес- ки использует эту библиотеку, если она вызывается на выполнение трансля- тором с языка программирования Си или транслятором с.языка програм- мирования Фортран 77. В приложении Г перечислены все эти библиотечные функции для версий ОС UNIX, используемых для ЭВМ семейства PDP-11. Для более детального знакомства с этими библиотечными функциями обратитесь к разд. 3 Руководства. 9.7. БИБЛИОТЕЧНЫЕ ФУНКЦИИ СПЕЦИАЛЬНОГО НАЗНАЧЕНИЯ Содержание и объем различных специализированных библиотек сущест- венно зависят от конкретной версии ОС UNIX, поэтому лучше всего обра- титься к имеющейся у Вас документации. Для использования Библиотеч- ных функций, входящих в состав этих библиотек, в программах на языках программирования Си или Фортран 77 необходимо использовать соответст- вующие флаги команд сс и f77. В приложении Г приведено краткое описа- ние двух таких библиотек, входящих в состав стандартной версии 7 OCUNIX. 9.7.1. ВЫСОКОТОЧНАЯ АРИФМЕТИКА Библиотечные функции, входящие в состав этой библиотеки, могут вы- полнять арифметические операции с целыми числами произвольной длины. Целые числа должны быть присвоены переменным некоторого предваритель- но определенного типа mint. Указатели на объекты типа mint следует ини- 216
циализировать с помощью библиотечной функции itom.функции, входящие в состав этой библиотеки, позволяют производить следующие арифмети- ческие операции: сложение, вычитание, деление, умножение, извлечение квадратного корня и возведение в целую положительную степень. 9.7.2. ГРАФИЧЕСКИЕ БИБЛИОТЕЧНЫЕ ФУНКЦИИ Графические библиотечные функции осуществляют вывод графической информации на стандартный вывод. Для указания команде сс на необходи- мость использования библиотеки графических функций последняя должна быть вызвана на выполнение с флагом —Iplot (фактически этот флаг пред- назначен команде Id). Замкнув стандартный вывод программы, использую- щей графические библиотечные функции, на стандартный ввод команды plot (описание этой команды приведено в plot (1)), Вы получите возмож- ность осуществить вывод графической информации на одно из перечислен- ных в plot (1) устройств отображения графической информации. Выбор конкретного устройства осуществляется с помощью соответствующего фла- га команды plot. Однако наряду с этим существует возможность непосред- ственного вывода графической информации на некоторые графические тер- миналы, например флаг —1300 команды plot указывает последней на необ- ходимость создания файла, содержащего графическую информацию и коман- ды управления графическим терминалом типа GSI 300, а флаг —14014 выполняет ту же задачу по отношению к графическому терминалу типа Tektronix 4014. Перед использованием любой графической библиотечной функции выход- ной поток должен быть инициализирован, для чего необходимо воспользо- ваться библиотечной функцией openpl . С помощью графических библиотеч- ных функций может быть сформирована информация для отображения пря- мых линий (line), окружностей (circle) и дуг (arc). Кроме этого, существу- ет возможность переместить перо (move) и вывести на устройство точку (point). После завершения всех операций вывода графической информации необходимо воспользоваться библиотечной функцией closepl, что приводит к принудительному выводу всей графической информации и закрытию фай- ла, открытого как поток данных для вывода. 9.8. АРХИВНЫЕ ФАЙЛЫ Если у Вас возникло желание создать собственную библиотеку, то може- те воспользоваться командой аг. Команда аг создает из содержимого не- скольких файлов так называемый архивный файл или библиотеку. Чаще всего она используется для создания библиотек из файлов, содержащих объектный код, которые в дальнейшем используются командой Id при ком- поновке программы. Вам должно быть понятно, что команда такого типа представляет собой весьма мощное средство. Так оно и есть. Команда аг дает возможность пользователю осуществлять следующие операции над 217
архивным файлом: создавать архивный файл, добавлять в архивный файл новые модули, извлекать модули из архивного файла и помещать их в от- дельные файлы, удалять модули из архивного файла. В принципе можно создать архивный файл из файлов, содержащих совершенно произвольную информацию. Если содержимое архивного файла используется командой Id, то он должен содержать модули, содержащие объектный код, например файл, со- держащий объектный код, полученный в результате вызова на выполнение команды сс с флагом —с: сс -с libfile.c или файл, содержащий объектный код, полученный в результате вызова на выполнение команды f77 с флагом —с: f77 -с libfile.f В результате выполнения обеих этих команд будет создан файл libfile.o-Что- бы добавить этот файл в существующий архивный файл, необходимо вы- звать на выполнение команду аг с флагом г. Если архивный файл с указан- ным именем не существует, то он будет создан, если же имя файла, добав- ляемого в архивный файл, совпадает с именем одного из модулей, содержа- щихся в существующем архивном файле, то последний будет заменен на добавляемый файл. Ввод с терминала следующей команды: ar г mylib filel.o file2.o file3.o приведет к созданию архивного файла myiib (если он не существовал ранее) и добавлению в него содержимого файлов filel.o, file2.o, file3.o. Получить список имен модулей, содержащихся в архивном файле, можно с помощью команды ar t mylib В результате выполнения этой команды на терминал будет выведено сле- дующее: filel .о file2.o file3.o Если Вы поместили в архивном файле объектные модули, то такой архив- ный файл ничем не отличается от любой из уже описанных библиотек ОС UNIX. Чтобы воспользоваться функциями из созданной Вами библиотеки, достаточно указать имя файла, содержащего ее, в списке аргументов ко- манд сс или f77, например: сс prog.с mylib В результате выполнения этой -команды содержимое файла prog.с будет передано на трансляцию, результат которой будет скомпонован с необходи- мыми функциями из библиотеки, содержащейся в файле mylib, и в файл a.out будет помещена выполняемая программа. 218
ГЛАВА 10. СОПРОВОЖДЕНИЕ ОС UNIX 10.1. ВВЕДЕНИЕ Употребив в названии этой главы термин "сопровождение", мы вовсе не имели в виду, что намерены углубиться в исследование проблемы техничес- кого сопровождения центрального процессора или периферийного обору- дования ЭВМ, как, впрочем, и проблемы сопровождения программного обеспечения в обычном смысле этого слова. Речь пойдет о широком круге самых общих вопросов, касающихся обеих этих проблем, не затронутых на- ми до сих пор. Мы поговорим о вещах, которые вряд ли необходимо знать случайным пользователям ОС UNIX, пропускающим обычно главы с описанием таких вопросов, как запуск и останов ОС UNIX, проверка целостности файловой системы и сохранение последней, механизм спуллинга при выводе на АЦПУ и т.д. Сказанное не означает, что Вы обязаны немедленно закрыть книгу, ус- покаивая себя соображением, что Ваши интересы распространяются лишь на пакеты прикладных программ, имеющиеся в составе ОС UNIX, хотя, посту- пив таким образом. Вы, возможно, сбережете свои нервы. Для успешного сопровождения ОС UNIX совершенно необходимо знать ряд деталей ее функционирования. Это поможет Вам понять, что же происходит с ОС UNIX в том или ином случае. Случайному пользователю ОС UNIX эта информация вряд ли понадобится (но, впрочем, и не повредит), тем же, в чьи обязаннос- ти входят ежедневные запуск и останов ОС UNIX, она просто необходима. 10.2. ЗАПУСК И ОСТАНОВ ОС UNIX Начиная этот параграф, мы хотим предупредить, что Вы не найдете в нем просто перечня инструкций для проведения запуска и останова ОС UNIX. Вместо этого мы предпочитаем обсудить особенности функционирования ОС UNIX на этапе ее запуска и на этапе ее останова. Такой подход единствен- но возможен, ибо конкретная версия ОС UNIX может отличаться от стан- дартной версии 7 ОС UNIX и, следовательно, инструкции типа" делайте это, а затем то" могут оказаться несостоятельными. Кроме того, такой подход позволяет описать несколько важных особенностей ОС UNIX, таких, на- пример, как программа init. Итак, давайте разберем всю процедуру в целом, начиная с запуска ОС UNIX и кончая ее остановом, посмотрим, что при этом происходит. 10.2.1. ЗАПУСК ОС UNIX Запуск ОС UNIX на "неживой" ЭВМ иногда напоминает обряды черной магии, поскольку в таких случаях обычно бормочут таинственные заклина- ния, трясут мешком с костями над источником питания и, наконец, умиро- творяют богов, принеся им в жертву девственницу, — Вам знакома эта кар- тина? Впрочем, здесь все зависит от конкретного аппаратного и программно- го обеспечения, поэтому детали могут и не совпадать. Ваша ЭВМ, может быть. 219
и не нуждается в потрясании костями и жертвоприношении, но в любом случае запуск ОС UNIX — это весьма торжественный момент, а потому Вы должны иметь о нем верное представление. Чтобы осуществить запуск конкретной версии ОС UNIX на кон- кретной ЭВМ, необходимо воспользоваться кон кретными доку- ментами, описывающими эту процедуру. Однако в любом случае, проделав эту процедуру. Вы получите копию ядра ОС UNIX в оперативной памяти ЭВМ, работающей отныне под ее управлением. На первое время полезно иметь кого-нибудь, кто уже прошел этот путь и сможет провести Вас по нему "за ручку". Мы опишем процедуру запуска стандартной версии 7 ОС UNIX на ЭВМ семейства PDP -111, используя ее в качестве примера и не забывая, что дета- ли этой процедуры зависят от типа используемой ЭВМ и конкретной версии ОС UNIX. Кроме того, мы опишем функции программных средств, участ- вующих в запуске ОС UNIX, потому что они практически не зависят от конкретных аппаратных и программных средств. Процедуру запуска ОС UNIX, как, впрочем, и процедуру запуска любой другой операционной систе- мы, принято условно разбивать на два шага: загрузку ОС UNIX и раскрут- ку ОС UNIX. Загрузка ОС UNIX на ЭВМ семейства PDP-11 — это красивая и доступная пониманию процедура. Ни один шаг ее не зависит от модели ЭВМ и от опе- рационной системы1 2. После того как Вы нажмете нужную кнопку на кон- соли {или на блоке процессора) ЭВМ, последняя произведет начальную загрузку и начнет выполнений программы загрузки ОС UNIX, которая вы- ведет на терминал свой промптер — символ @. В результате в Вашем распо- ряжении окажется целый пакет так называемых автономных программ (т.е. программ, не требующих для своего выполнения наличия операционной си- стемы). С помощью программ, входящих в состав этого пакета. Вы можете, например, форматировать диски и диагностировать работоспособность ЭВМ. Однако не будем отвлекаться, ибо наша с Вами задача — осуществить запуск ОС UNIX. Как мы уже сказали, запуск ОС UNIX представляет собой двухшаговую процедуру, выполняемую пользователем с помощью промежуточной про- граммы boot. Основной функцией программы boot является отыскание файла, содержащего ядро ОС UNIX, и перемещение ядра ОС UNIX в опера- 1 Необходимо отметить, что процедура запуска ОС UNIX версии 7 отличается от процедуры запуска ОС UNIX версии 6. (Прим, ред.} 2 Авторы несколько преувеличивают единообразие процедуры загрузки; так, на ЭВМ PDP-11/44 загрузка действительно производится нажатием одной кнопки, на ЭВМ PDP-11/34 для загрузки необходимо ввести с консоли адрес аппаратного загрузчика, а на ЭВМ PDP-11/23 загрузка производится автоматически сразу после включения пита- ния. (Прим, ред.) 220
тивную память. Программа boot представляет собой автономную интер- активную программу. На нашей ЭВМ диалог с программой boot имеет следующий вид: @boot boot : rf(0,0)unix Итак, увидев на терминале символ ©, введите с терминала имя программы boot, в результате чего на терминал будут выведены имя программы boot и символ : . Теперь введите с терминала информацию, указывающую програм- ме boot, где искать файл, содержащий ядро ОС UNIX: стандартное имя типа диска, номер накопителя, номер сегмента диска и имя файла, содержащего ядро ОС UNIX. Таким образом загруженная и раскрученная1 ОС UNIX инициализирует себя и вызывает на выполнение интерпретатор команд shell, предоставив ему для диалога с пользователем консольный терминал. Вызванный на вы- полнение интерпретатор команд shell предназначается администратору ОС UNIX, в обязанности которого входит процедура проверки целостности файловой системы, описываемая ниже1 2. Эту процедуру следует проводить в обязательном порядке после каждого нового запуска ОС UNIX, так как нет никаких гарантий, что предыдущий останов ОС UNIX был осуществлен кор- ректно. Непредвиденный останов ЭВМ (неисправности питания или какие-ли- бо другие причины) может нарушить целостность файловой системы, причем степень нарушения целостности файловой системы быстро нарастает, если Вы позволили ОС UNIX функционировать в таком виде и не позаботились о восстановлении целостности файловой системы. Короче говоря, чтобы запустить ОС UNIX, Вам необходимо: нажать нужную кнопку на консоли ЭВМ; ввести с терминала требуемую информацию; проверить целостность файловой системы. Если окажется, что целостность файловой системы нарушена, то эксплуати- ровать ОС UNIX нельзя до тех пор, пока не будет восстановлена целостность файловой системы. Нам до сих пор не приходилось оказываться в подобной ситуации, но, быть может нам просто везло? Для того чтобы ядро ОС UNIX оказалось способным вызвать на выполнение интерпретатор команд shell. 1 Авторы пока еще не сказали практически ничего о процессе раскрутки ОС UNIX. (Прим, ред.) 2 Критерии целостности файловой системы будут введены немного позднее. (Прим, ред.) 221
достаточно, чтобы неразрушенными остались: корневой каталог файловой системы, несколько каталогов, ближайших к нему по дереву файловой систе- мы, и некоторые из содержащихся в них файлов. Если Вам не удается полу,- чить на терминале промптер интерпретатора команд shell, есть подозрение, что разрушенным оказался один из указанных каталогов, и, если это дейст- вительно так, — у Вас большое горе. Но предположим, что целостность файловой системы не нарушена. В этом случае введите с терминала текущие дату й время (для чего изучите описание команды date, приведенное в Руководстве), а затем нажмите кла- вишу, соответствующую специальному символу EOT. Последняя процедура переведет ОС UNIX в обычный многопользовательский режим работы. По- старайтесь не ошибиться при вводе даты, так как введенная неверно дата может внести полный беспорядок в файловую систему ОС UNIX1. 10.2.2. ПРОГРАММА init Успешный запуск ОС UNIX во многом зависит от того, насколько успеш- но будет выполнена специальная программа init. Программа init—это весь- ма своеобразная программа. С одной стороны, она ничем не отличается от любой команды ОС UNIX, а с другой стороны, представляет собой неотъем- лемую часть самой ОС UNIX. Ядро ОС UNIX, перемещенное в оперативную память и получившее управление, порождает некоторый специальный про- цесс, присваивая ему идентификатор процесса 0, хотя в действительности его нельзя назвать процессом в обычном смысле. Затем этот псевдопроцесс порождает первый настоящий процесс с идентификатором процесса 1. Пер- вым действием этого нового процесса является выполнение системного вы- зова ехес, который обеспечивает смену программы, определяющей поведе- ние этого процесса, на программу, хранящуюся в файле /etc/init. Одной из задач процесса с идентификатором процесса 1 является вызов на выполнение интерпретатора команд shell, в результате чего на консольном терминале появляется промптер интерпретатора команд shell и Вы осознаете, что ОС UNIX жива. Таким образом, интерпретатор команд shell инициирует- ся процессом, в рамках которого выполняется программа init. Если интер- претатор команд shell завершает свою работу (а именно это происходит в результате нажатия клавиши, соответствующейспециальномусимвОлу EOT), процесс, в рамках которого выполняется программа init, делает из этого вывод, что все готово к использованию ОС UNIX в многопользовательском режиме, и предпринимает в связи с этим некоторые шаги. На первом шаге процесс, в рамках которого выполняется программа init, вновь иници- 1 Просмотрите еще раз описание команды make в гл. 6. (Прим, ред.) 222
ирует интерпретатор команд shell и передает ему на выполнение командный файл /etc/rc так, как если бы указанный командный файл был передан на выполнение интерпретатору команд shell привилегированным пользовате- лем. Результатом этого может быть удаление некоторых временных файлов и каталогов, подключение дополнительных томов файловой системы или порождение нескольких служебных процессов1. На последнем шаге процесс, в рамках которого выполняется программа init, производит ввод из файла /etc/ttys, содержащего информацию обо всех терминалах, с которых пользователи ОС UNIX могут осуществить вход в ОС UNIX. Для каждого из этих терминалов процесс, в рамках которого выполняется программа init, порождает новый процесс. Каждый из этих вновь порожденных процессов открывает соответствующий ему терминал трижды: как стандартный ввод, стандартный вывод и стандартный протокол, в результате чего появляется возможность использования соответствующих пользовательских файлов1 2. После этого вызывается на выполнение про- грамма getty, хранящаяся в файле /etc/getty, это первая программа ОС UNIX, обращающаяся к пользователю ОС UNIX. В ее задачу входит авто- матическое определение таких характеристик терминала, как скорость об- мена информацией и возможность работы со строчными буквами. Первое увиденное Вами на терминале сообщение Login также выводится програм- мой getty. После того как Вы ввели с терминала Ваше входное имя, в ответ на это сообщение программа getty вызывает на выполнение программу login, передав ей в качестве аргумента введенное Вами имя. Отсутствие в Вашем входном имени строчных букв позволит программе getty сделать за- ключение, что используемый Вами терминал не обеспечивает возможности работы со строчными буквами. Что произойдет вслед за этим. Вы уже знаете. Процесс с идентификатором процесса 1 никаких действий не выполняет, но ожидает завершения порожденных им процессов. Процесс с идентифика- тором процесса 1 — это особый процесс, который берет на себя опеку над всеми процессами-сиротами (процессами, у которых завершились процессы их породившие), т.е. является тем самым всеобщим предком, о котором шла речь в гл. 8. Особенно пристальным вниманием процесса с идентифика- тором процесса 1 пользуются процессы, порожденные им для каждого тер- минала; если один из этих процессов завершается, то вместо него немедленно 1 Более детально с двумя основными служебными процессами можно познакомить- ся, изучив сгоп (8) и update (8) в Руководстве. 2 Все указанные здесь операции проделываются, конечно же, не с терминалами, а с соответствующими им специальными файлами. (Прим, ред.} 223
порождается новый процесс, который выводит на терминал очередное сооб- щение Login. Из сказанного можно сделать очень важный вывод: интерпретатор команд shell, промптер которого Вы видите на терминале после входа в ОС UNIX, является по сути дела одним из процессов, порожденных процессом с иден- тификатором процесса 1. Именно поэтому завершение выполнения интер- претатора команд shell гарантирует появление на терминале сообщения Login. Без программы init ОС UNIX вообще нельзя было бы запустить, не го- воря уже о том, чтобы заставить ее правильно функционировать. Процесс с идентификатором процесса 1 занимает в ОС UN IX особое место и не должен завершаться никогда. Некоторые версии ОС UNIX во время функциониро- вания используют и другие специальные процессы. Например, версия ОС UNIX для ЭВМ VAX-11 для своих собственных нужд использует процесс с идентификатором процесса 2. Обратитесь по этому вопросу к имеющейся у Вас документации. 10.2.3. ОСТАНОВ ОС UNIX Поняв, как функционирует ОС UNIX, не трудно понять, как остановить ОС UNIX. Для этого достаточно убить процесс, в рамках которого выпол- няется программа ink, не порождать более процессов, а затем завершить все выполняющиеся в ОС UNIX процессы. Процедура эта чрезвычайно проста и выполняется самой программой init после получения процессом с иденти- фикатором процесса! сигнала SIGHUP. Чтобы послать процессу с идентифи- катором процесса 1 сигнал SIGHUP, Вы должны войти в ОС UNIX в качестве администратора ОС UNIX (или, иначе говоря, в качестве привилегирован- ного пользователя ОС UNIX) и ввести с терминала команду kill -1 1 В результате выполнения этой команды ОС UNIX будет переведена в одно- пользовательский режим, после чего на консольный терминал будет выведен промптер интерпретатора команд shell. Процесс, в рамках которого выпол- няется программа init, все-таки породил еще один, на сей раз последний процесс. Теперь введите с консольного терминала пару раз команду sync и остановите ЭВМ. Ввод команды sync необходим для того, чтобы гарантиро- вать сохранение целостности файловой системы после останова ЭВМ. За- будьте ввести с терминала команду sync — и Вы получите великолепный шанс приобрести опыт в восстановлении целостности файловой системы. 224
10.3. ФАЙЛ /etc/ttys И ПРОГРАММА getty Файл /etc/ttys играет важную роль для ОС UNIX, функционирующей в многопользовательском режиме. Информация, содержащаяся в файле /etc/ttys, сообщает ОС UNIX, терминалы каких типов подключены к ЭВМ и что с ними, вообще говоря, делать. Когда ОС UNIX запускается в много- пользовательском режиме, процесс, в рамках которого выполняется про- грамма init, вводит эту информацию из файла /etc/ttys, определяет число процессов, которые необходимо породить, и проделывает это способом, который мы только что описали. В стандартной версии 7 ОС UNIX файл /etc/ttys имеет простой формат — каждая строка этого файла содержит информацию, касающуюся одного тер- минала. Первый символ каждой строки файла /etc/ttys определяет, будет ли использоваться терминал для входа в ОС UNIX: 1, если терминал будет использоваться для входа в ОС UNIX, и 0 в противном случае. Второй сим- вол каждой строки файла /etc/ttys передается программой init программе getty в качестве аргумента и используется последней для определения неко- торых технических характеристик терминала. Остальные символы каждой строки файла /etc/ttys составляют имя соответствующего терминалу специ- ального файла из каталога /dev. Например, строка файла /etc/ttys может выглядеть следующим образом: 10tty 1 Информация, содержащаяся в этой строке, означает, что процесс, в рамках которого выполняется программа init, откроет специальный файл /dev/tty1 как стандартный ввод, стандартный вывод и стандартный протокол, после че- го породит процесс для терминала, соответствующего специальному файлу /dev/tty 1, который унаследует открытые файлы, а на терминал, которому со- ответствует специальный файл /dev/ttyl, будет веэдено сообщение Login. Процесс, в рамках которого выполняется программа init, производит ввод информации из файла /etc/ttys лишь при запуске ОС UNIX, поэтому любые изменения содержимого файла /etc/ttys во время работы ОС UNIX остаются незамеченными процессом, в рамках которого выполняется про- грамма init. Из этого, очевидно, следует, что ожидать какой-либо реакции со стороны ОС UNIX сразу после редактирования файла /etc/ttys бессмыс- ленно. Добиться этого можно, лишь заставив процесс, в рамках которого выполняется программа init, еще раз ввести информацию из этого файла, для чего необходимо послать процессу с идентификатором процесса 1 сигнал SIGTERM , введя с терминала следующую команду: kill 1 8 Зак 1165 225
Как мы уже упоминали, второй символ каждой строки содержимого файла /etc/ttys передается процессом, в рамках которого выполняется программа init, программе getty в качестве аргумента. Что будет с,ним делать програм- ма getty, зависит от конкретной версии ОС UNIX, поэтому Вам лучше всего обратиться за разъяснениями к документации, описывающей используемую Вами версию ОС UNIX. Обычно программа getty использует этот символ для определений специальных характеристик терминала, таких как метод контроля входной и выходной информации, значение скорости обмена ин- формацией и т.д. Попытаемся описать эту процедуру более подробно. Итак, программа getty выводит на терминал сообщение Login и ожидает ответа пользователя. Если таким ответом будет нажатие клавиши break на терминале, то про- грамма getty произведет установку нового значения скорости обмена ин- формацией с данным терминалом. При этом программа getty перебирает циклически стандартный набор значений скорости обмена информацией с терминалом, так что пользователю остается лишь нажимать клавишу break на терминале в ответ на невразумительные сообщения программы getty до тех пор, пока на терминале пользователя не появится наконец сообщение Login. После этого пользователь ОС UNIX может быть уверен, что значение скорости обмена информацией с его терминалом установлено правильно. Вы уже знаете, каким должен быть ответ на появившееся на терминале со- общение Login. Если введенное Вами входное имя состоит только из про- писных букв латинского алфавита, то программа getty решит, что аппарат- ные средства Вашего терминала не позволяют использовать строчные буквы латинского алфавита и впредь весь обмен информацией с Вашим термина- лом будет вестись только в режиме прописных букв. Если ввод с терминала входного имени Вы завершите нажатием клавиши return вместо используе- мой обычно для этой цели клавиши newline, то этот факт также будет отме- чен программой getty и впредь каждый введенный Вами с терминала символ return будет интерпретироваться как символ newline. И последним действием программы getty является вызов ею на выполне- ние программы login и передача последней введенного Вами входного имени в качестве параметра. С этого момента работой Вашего терминала управляет программа login. Берегитесь, если специальные характеристики Вашего терминала были определены программой getty неверно. 10.3.1. ГРУППА ПРОЦЕССОВ Всякий процесс, не являющийся до сих пор членом некоторой группы процессов, автоматически причисляется к ней с того момента, как этот про- цесс впервые откроет некоторый терминал для проведения операций ввода- вывода1. Всякий процесс-потомок причисляется к той же группе процессов, 1 Здесь, как и ранее, авторы имеют в виду, что процесс открывает не терминал, а соответствующий ему специальный файл. (Прим, ред.) 226
что и породивший его процесс-предок, и принадлежность эта не Может быть изменена. Таким образом, все процессы, соответствующие пользовательским программам или командам ОС UNIX, которые инициированы с некоторого терминала, причисляются к одной и той же группе процессов, ассоциирован- ной с этим терминалом, ибо все они являются процессами-потомками про- цесса, порожденного для этого терминала процессом с идентификатором процесса 1.Терминал, с которым ассоциирована некоторая группа процессов, называется управляющим терминалом для данной группы процессов. При- надлежность некоторого процесса к группе процессов, для которой данный терминал является управляющим терминалом, определяет реакцию этого процесса не нажатие клавиши, соответствующей специальному символу INTERRUPT на терминале. Нажмите на Вашем терминале клавишу, соот- ветствующую специальному символу INTERRUPT и всем процессам,причис- ленным к группе процессов, для которой Ваш терминал является управля- ющим терминалом (даже тем, о существовании которых Вы и не подозре- ваете) будет послан сигнал SIGINT. 10.3.2. ОПРАВДАНИЯ Мы описали некоторые детали процедуры запуска стандартной версии 7 ОС UNIX, так как считаем знание их весьма полезным. Должны предупре- дить Вас, что процедура запуска ОС UNIX — это первое, что подвергается модификации поставщиками ОС UNIX и подобных ей операционных систем, поэтому очень может быть, что Вы столкнетесь с небольшими (или наобо- рот — значительными) отличиями процедуры запуска используемой Вами версии ОС UNIX на используемой Вами ЭВМ от той процедуры, которую мы здесь описали. Попытайтесь выяснить это заранее, для чего изучите описа- ния программ init, getty и файла /etc/ttys, приведенные в имеющейся у Вас документации1. 10.4. ПРОВЕРКА ЦЕЛОСТНОСТИ ФАЙЛОВОЙ СИСТЕМЫ Мы уже говорили, что Вы должны проверять целостность файловой сис- темы при каждом новом запуске ОС UNIX. Зачем? Вероятно, созданные файлы дороги Вам и, потеряв их, Вы вряд ли будете счастливы. Чтобы этого не случилось, к файловой системе в ОС UNIX необ- ходимо относиться с уважением и вниманием. Файловая система-ОС UNIX представляет собой хорошо сконструированный набор данных. Для правиль- ного функционирования файловой системы необходимо поддержание ее в должном порядке. Обычно ОС UNIX сама следит за порядком в файловой системе, однако иногда происходят события, вынуждающие ОС UNIX пос- 1 Воспользоваться этой рекомендацией чрезвычайно трудно, так как ни один из из- вестных источников не содержит сколько-нибудь толкового и полного их описания. (Прим, ред.) 227
тупать прямо противоположным образом1. Как правило, это обусловлено неисправностями накопителя на магнитных дисках и/или неисправностями прочей аппаратуры ЭВМ (чаще всего это неполадки с питанием) Не следует также исключать вероятность того, что какой-нибудь идиот "распишет" Вам весь диск "по ошибке". Как известно, защиты ''от дурака "нет, однако ис- пользовать все доступные Вам способы защиты и попытаться запретить дос- туп по записи к дискам, на которых расположена файловая система, Вы обязаны. Неисправности аппаратуры ЭВМ чрезвычайно опасны, так как некоторая очень важная информация, например модифицированное содержимое не- которого каталога, попадает на диск не сразу. Дело в том, что для уменьше- ния времени доступа к подобной информации она хранится не на внешнем запоминающем устройстве, которым является диск, а в оперативной памяти, что, безусловно, повышает вероятность потери этой информации при воз- никновении неисправностей в аппаратуре ЭВМ. Вывод на диск информации, хранящейся в оперативной памяти, происходит лишь в том случае, когда пользователь ОС UNIX дает специальное на то указание или же когда опера- тивная память необходима ОС UNIX для проведения других работ. Для об- новления информации, хранящейся на диске, можно воспользоваться систем- ным вызывом sync. Если Вы хотите запустить ОС UNIX корректно, то в состав командного файла /etc/rc следует ввести имя команды /etc/update. Это приведет к порождению процесса, соответствующего указанной команде ОС UNIX, единственной задачей которого будет регулярное выполнение (примерно каждые 30 с) системного вызова sync. Для однократного об- новления информации, хранящейся на диске, достаточно ввести с терминала команду sync, выполнение которой по сути дела сводиться к изданию систем- ного вызова sync. Как мы уже говорили, команда sync должна быть послед- ней командой, введенной с терминала перед остановом ЭВМ. В ОС UNIX файловая система лишена "спасательных" средств, подобных тем, какие имеют файловые системы других операционных систем. Такие методы предохранения файловой системы от разрушения, как дублирование со- держимого каталогов и файлов, имеющих контрольные суммы, совершен- но чужды ОС UNIX (почему бы им, однако, и не появиться в будущих вер- сиях ОС UNIX). Единственное, что Вы можете проверить, — это структуру файловой системы. Никаких средств проверки сохранности отдельных фай- лов ОС UNIX своим пользователям не предоставляет. Этот факт должен навести пользователей ОС UNIX на мысль, что неплохо было бы обзавестись собственными средствами контроля, такими, как, например, контрольные суммы отдельных файлов или копии содержимого некоторых файлов, раз 1 Необходимо отметить, что в отличие от других операционных систем ОС UNIX в процессе своей работы не осуществляет контроля возникающих по'тем или иным при- чинам нарушений целостности файловой системы. (Прим, ред.) 228
уж ОС UNIX не берет на себя заботу о сохранности файлов своих пользова- телей. Подобный подход к вопросу обеспечения безопасности файловой системы в ОС UNIX основан на следующих соображениях: избежать потери данных и тем самым разрушения файловой системы в случаях неисправности аппаратуры ЭВМ все равно не удастся; создавать же специальные средства защиты файловой системы невыгодно, так как это существенно понизит скорость реакции ОС UNIX, что будет весьма трудно компенсировать самой изощренной защитой. Мы склонны согласиться с таким подходом, однако хотим напомнить Вам, что высказали лишь нашу личную точку зрения. ю.4.1. команды icheck и dcheck Рассмотрим два наиболее часто используемых в ОС UNIX средства про- верки целостности файловой системы: команды icheck и dcheck. Если Вы собираетесь использовать, указанные команды, то необходимо тщательно изучить описания этих команд, приведенные в Руководстве. Мы тоже прове- ряем целостность файловой системы нашей ОС UNIX с помощью этих двух команд. Процедура проверки целостности файловой системы имеет у нас следующий вид: каждый диск, на котором расположена файловая система, подвергается проверке с помощью команды icheck, затем обнаруженные командой icheck нарушения целостности файловой системы устраняются, после чего информация, хранящаяся на данном диске, подвергается оконча- тельной проверке с помощью команды dcheck. Для понимания того, какие действия и как выполняются командами icheck и dcheck, необходимо знать, каким образом файловая система ОС UNIX располагается надиске. Ну что ж, давайте изучим этот вопрос. Команда icheck применяется для проверки правильности использования блоков диска файловой системой. Например, блок диска, предназначенный для будущего размещения в нем содержимого некоторого файла (и потому указанный в списке свободных блоков), не должен одновременно исполь- зоваться файловой системой для хранения содержимого файлов, так же как один и тот же блок диска не должен использоваться файловой системой для хранения содержимого более чем одного файла. Кроме того, команда icheck осуществляет проверку того, что список свободных блоков не содержит но- меров блоков диска, не существующих на диске данного типа (если полный объем имеющегося у Вас диска составляет 10 000 блоков, то в списке сво- бодных блоков не должно быть блока диска с номером 10 001), а файловая система не использует для хранения содержимого файлов блоков диска с такими номерами. Блоки диска, не фигурирующие в списке свободных блоков и не используемые файловой системой для хранения содержимого файлов, трактуются командой icheck как блоки типа missing. Наличие таких блоков диска говорит о нарушении целостности файловой системы. Ни в коем случае нельзя применять команду icheck для проверки целостности 229
файловой системы, функционирующей в данный момент ОС UNIX, ибо в противном случае сообщения, выведенные на терминал командой icheck, не будут соответствовать истине. Проверка целостности части файловой систе- мы, расположенной на отдельном диске, и восстановление ее в случае необ- ходимости должны быть выполнены до того, как эта часть файловой систе- мы будет включена в состав файловой системы функционирующей ОС UNIX. К нашей ЭВМ подключены три накопителя на магнитных дисках типа RK07. Соответствующие им блокориентированные специальные файлы в каталоге /dev имеют имена rfO, rf 1 и rf2. Кроме этого, в каталоге /dev со- держатся соответствующие этим дискам байториентированные специальные файлы с именами rrfO, rrf 1 и rrf2. Для ускорения процесса проверки це- лостности файловой системы, расположенной на этих дисках, мы обычно пользуемся байториентированными специальными файлами, так как в этом случае команда icheck получает возможность за одну операцию ввода-вывода ввести с диска информацию большего объема, нежели в случае использова- ния блокориентированных специальных файлов1. При этом мы пользуемся следующей командой: icheck /dev/rrf 1 В результате выполнения команды icheck на терминал будут выведены не- которые характеристики проверявшейся части файловой системы. Ваша задача — понять эту информацию и в случае необходимости восстановить на- рушенную целостность файловой системы. Если в результате применения команды icheck окажется, что список свободных блоков содержит блоки типа missing, типа dup или типа bad, введите команду icheck —s /dev/rrf 1 Это приведет к составлению заново списка свободных блоков — и проблема будет решена. Гораздо хуже, если блоки типа dup или типа bad ассоцииро- ваны с некоторыми описателями файлов (т.е. хранят содержимое некоторых файлов). Очень чэсто это означает, что описатели файлов, а вместе с ними и сами файлы утеряны навсегда. Допустим, что описатель файла с номером 153 содержит указатели на блоки типа bad или типа dup. В таком случае прежде всего необходимо вы- яснить, какие входы в каталоги ссылаются на данный описатель файла (т.е. содержат в поле ссылки число 153). Для этого введите команду ncheck -i 153 /dev/rrf1 В результате на терминал будет выведен список имен файлов. Если Вам очень не хочется терять содержимое некоторого файла с именем из выве- денного списка, попробуйте подключить другой том файловой системы (что означает эта фаза, мы объясним несколько позднее) и скопировать в него содержимое этого файла. Понятно, что нельзя надеяться на успех, если 1 Специальные файлы будут рассмотрены несколько позднее. (Прим, ред.) 230
описатель файла с номером 153 содержит указатели на "испорченные" блоки диска. Теперь запишите на бумаге имена файлов из списка, о котором толь- ко что шла речь (они Вам еще пригодятся) и очистите описатель файла с но- мером 153, для чего введите команду clri /dev/rrf1 153 Теперь описатель файла с номером 153 свободен, следовательно, свободны и все те блоки диска, указатели на которые он содержал, однако в списке свободных блоков диска они не фигурируют, следовательно, следующим шагом необходимо ввести команду (check - s /dev/rrf1 в результате выполнения которой список свободных блоков будет составлен заново. Итак, подводя итоги, перечислим вновь последовательность Ваших дейст- вий: проверьте целостность файловой системы с помощью команды icheck; запомните (или запишите) имена файлов, соответствующие всем "подо- зрительным "описателям файлов; очистите все "подозрительные " описатели файлов с помощью команды clri; введите с терминала еще раз команду icheck с флагом —s для составле- ния заново списка свободных блоков. Можете еще раз выполнить команду icheck, дабы убедиться, что все в порядке. Описанную процедуру проверки целостности файловой системы необходимо проделать для каждой части файловой системы, хранящейся на отдельном диске. Перейдем к описанию процедуры проверки целостности файловой систе- мы с помощью команды dcheck. Эта команда применяется для сопоставле- ния числа ссылок на данный описатель файла с числом входов в каталоги, содержащих в поле ссылки номер данного описателя файла. Команда dcheck выводит на терминал лишь информацию о фактах несовпадения этих двух величин. Если результаты применения команды dcheck оказались таковы, что число ссылок на некоторый описатель файла, содержащееся в самом описателе файла, превосходит число входов в каталоги, ссылающихся на этот описатель файла, не пугайтесь — ситуация еще не критическая и, если у Вас нет свободного времени, оставьте все как есть. Другое дело, если Вы получили обратную картину. В этом случае необходимо немедленно заняться восстановлением целостности файловой системы. Для начала выясните, какие конкретно входы в каталоги содержит ссылки на неблагополучный описатель файла, точно так же, как Вы это делали при использовании коман- ды icheck. После этого повторите обычную процедуру: подключите новый том файловой системы, скопируйте в него файлы, соответствующие неблаго- получным входам в каталоги, очистите неблагополучный описатель файла, удалите ссылающиеся на него входы в каталоги, после чего можете в заклю- 231
чение скопировать обратно интересующие Вас файлы и в случае необходи- мости восстановить с помощью команды In синонимы их имен1. Самыми ответственными шагами этой процедуры являются очистка не- благополучного описателя файла и удаление входов в каталоги, содержащих ссылки на этот описатель файла. Если в результате выполнения команды dcheck выяснилась необходи- мость использования команды clri, то Вам необходимо вновь воспользовать- ся командой icheck, так как в результате всех этих манипуляций наверняка возникнут блоки типа missing. В качестве непреложного предлагаем Вам следующее правило: можете терпеть несколько дюжин блоков типа missing, кучу описателей файлов с числом ссылок на них, превосходящим число входов в каталоги, ссылаю- щихся на эти описатели файлов, пока Вам не надоест разглядывать на тер- минале диагностические сообщения. Но все отклонения другого характера должны немедленно устраниться. И последнее замечание о команде dcheck. Во время выполнения команда dcheck использует информацию, хранящуюся в суперблоке файловой систе- мы, например максимально возможный номер блока диска и описателя файла. Если окажется, что суперблок файловой системы испорчен, то коман- да dcheck будет прямо-таки "вне себя ". Вы, конечно, можете ’'залатать" суперблок файловой системы с помощью команды adb, но нам кажется, что проще взять новый диск, создать на нем пустую файловую систему2 с по- мощью команды mkfs и скопировать в нее все содержимое файловой систе- мы с испорченным суперблоком. После этого проверьте целостность вновь созданной файловой системы с помощью команды icheck и немедленно остановите ЭВМ, не выполняя команды sync. Если Вы не сделаете это- го, то неверная информация, от которой Вы так.хотели избавиться, будет скопирована в суперблок только что созданной Вами файловой системы и все Ваши труды пропадут даром. В различное время и с различным успехом предпринимались попытки создания для ОС UNIX автоматического корректора целостности файловой системы. Так, команда fsck, вошедшая в состав версии ОС UNIX, разрабо- танной в Калифорнийском университете (г. Беркли), наверное, хорошая штука, вполне заслуживающая распространения, но и она не в состоянии справиться со всеми потенциально возможными нарушениями целостности файловой системы ОС UNIX. Очевидно, файловая система — это та область, где еще не скоро можно будет обойтись без вмешательства человека, где, как сказано в Руководстве, "опытиосознанныйрискещеоченьпригодятся". 1 См. разд. 5.3. (Прим, ред.) Под пустой файловой системой авторы понимают файловую систему содержащую лишь пустые каталоги. (Прим, ред.) 232
10.4.2. СОЗДАНИЕ ПУСТОЙ ФАЙЛОВОЙ СИСТЕМЫ Время от времени перед Вами непременно будет вставать задача создания новой пустой файловой системы. Обычно пустой файловой системой поль- зуются для воссоздания файловой системы на диске, для чего все файлы файловой системы копируются с магнитной ленты, где они обычно хранят- ся, в пустую файловую систему, заранее созданную на диске. Для создания пустой файловой системы необходимо воспользоваться командой mkfs. Вообще говоря, команда mkfs способна выполнять и более сложные работы, но мы используем ее только для создания пустой файловой системы. Для этого введите с терминала команду /etc/mkfs /dev/rrf1 50000 после чего на диске, которому соответствует специальный файл/dev/rrf 1, первые 50 000 блоков диска будут заняты пустой файловой системой. После этого можно воспользоваться командой restor, которая восстанавливает на диске файловую систему, копируя с магнитной ленты все хранящиеся на ней файлы в созданную пустую файловую систему. 10.5. СОХРАНЕНИЕ ФАЙЛОВОЙ СИСТЕМЫ Представим себе следующую ситуацию: Вы купили ЭВМ, в составе кото- рой имеется лишь одно внешнее запоминающее устройство — накопитель на магнитном диске типа "Винчестер"1. Вы спокойно и счастливо эксплуатиру- ете свою ЭВМ, поздравляя себя с тем, что сэкономили кучу денег, отказав- шись от приобретения накопителя на магнитной ленте и еще одного накопи- теля на магнитном диске. Ваша ОС UNIX работает отлично, и месяц за меся- цем вся Ваша система функционирует без единого сбоя. И вот однажды один из привилегированных пользователей, должно быть с похмелья, производит запись на Ваш единственный диск и "совершенно случайно" именно в те блоки диска, где хранятся и программа boot, и суперблок файловой системы, и несколько сотен описателей файлов. Вполне естественно Вы зададите нам вопрос: "Что же теперь делать?". Отвечаем: "Плачьте!" Во избежание подобных ситуаций авторы ОС UNIX создали две специаль- ные программы: dump и restor, с помощью которых можно сохранить копию файловой системы на некотором внешнем запоминающем устройстве и при необходимости воспользоваться этой копией для восстановления файловой системы. Обе эти программы детально, хотя и весьма невразумительно, описаны в Руководстве. Применение программ dump и restor предполагает, вообще говоря, использование в качестве внешнего запоминающего уст- ройства накопителя на магнитной ленте, а в качестве носителя информа- 1 Накопитель на магнитном диске типа "Винчестер” представляет собой устройство внешней памяти, в котором конструктивно имеется лишь один фиксированный (т.е. несменный) носитель информации. (Прим, ред.) 233
ции — магнитной ленты, однако вполне приемлемо использование любого другого устройства внешней памяти блочной организации, например накопи- теля на магнитных дисках (носителем информации в этом случае может быть сменный пакет дисков). Мы надеемся, что теперь Вы будете осмотрительнее и, прежде чем расста- ваться с деньгами, постараетесь подробно выяснить у поставщика, какими средствами сохранения и восстановления файловой системы обладает при- обретаемая Вами версия ОС UNIX. Мы попытаемся подробно описать процедуру сохранения и восстановле- ния файловой системы, которая принята у нас. Наши труды в этой области ни в коей мере не претендуют на фундаментальность, тем не менее мы можем с гордостью заявить: "До сих пор мы ни разу не потеряли важной информа- ции!" За три года эксплуатации ОС UNIX мы лишь трижды восстанавливали файловую систему, воспользовавшись для этого ее копией, сохраненной на магнитной ленте. Первый раз это произошло после того, как мы ло дешевке купили новую экспериментальную ЭВМ; две другие неудачи были связаны с нашими попытками модифицировать ядро ОС UNIX, которое после этого почему-то перестало работать. Надо признать, что случаи фатального разру- шения файловой системы крайне редки, поэтому мы не станем уделять процедуре сохранения и восстановления файловой системы много внимания, хотя быть может, она его и заслуживает1. 10.5.1. ПРОГРАММА dump Каждый том файловой системы эксплуатируемой нами ОС UNIX "сбро- шен" на отдельную магнитную ленту. В результате мы стали обладателями целой коллекции магнитных лент. Процедуру сохранения файловой системы желательно производить до того, как рядовые пользователи ОС UNIX начнут входить в нее, в противном случае те файлы, которые модифицируются в момент вызова Вами на выполнение программы dump, будут сохранены на- половину "новыми ", а наполовину "старыми". Мы, например, производим процедуру сохранения файловой системы глубокой ночью, так как это удобно и нам, и рядовым пользователям ОС UNIX, которые в это время, как правило,спят. А теперь прочтите приводимое ниже описание программы dump и сопос- тавьте его с соответствующей страницей Руководства. Для начала рассмотрим команду, позволяющую сохранить весь том фай- ловой системы на магнитной ленте: dump Ou /dev/rfO В результате применения приведенной выше команды на магнитной ленте, установленной на накопитель с номером 0, будет целиком сохранена файло- 1 Хочетсв напомнить читателю, что подобное утверждение является следствием субъективного опыта авторов. (Прим, ред.) 234
вая система, хранящаяся на диске, соответствующем специальному файлу /dev/rfO. Результат применения этой команды обычно называют полным дампом. После этого мы сохраняем на другой магнитной ленте все те файлы, которые были модифицированы пользователями ОС UNIX за время, про- шедшее с момента получения предыдущего полного дампа. Для этого мы используем команду dump 9u /dev/rfO Результат применения этой команды обычно называют дампом изменений. Схема наших действий по сохранению файловой системы такова: мы от- водим три магнитные ленты (обозначим их для простоты например, МЛ1, МЛ2 и МЛЗ) для сохранения дампов изменений и одну магнитную ленту для сохранения полного дампа. Для сохранения очередного дампа изменений мы используем очередную магнитную ленту, чередуя магнитные ленты в следующей последовательности: МЛ1, МЛ2, МЛЗ, МЛ1, МЛ2, МЛЗ, . . . Про- цедуру получения дампа изменений проводим ежедневно; когда объем очередного дампа изменений приблизится к трети объема полного дампа, мы проводим новую процедуру получения полного дампа, используя для этого магнитную ленту, отведенную для сохранения полного дампа. Подобная схема полностью отвечает нашим потребностям и гарантирует нам, что даже при полном разрушении файловой системы больше "работы одного дня" мы не потеряем. 10.5.2. программа restor Процедура восстановления файловой системы, если она была предвари- тельно сохранена, чрезвычайно проста. Если какой-нибудь том файловой системы разрушен настолько, что мы не в состоянии или просто не хотим восстановить его с помощью команды adb, то прежде всего мы создаем пустую файловую систему на новом диске, используя для этого команду mkfs: /etc/mkfs /dev/rrf1 50000 затем устанавливаем магнитную ленту, на которой был сохранен полный дамп на накопитель с номером 0 и вводим с терминала команду restor г /dev/rf1 После завершения этой операции мы устанавливаем на накопитель с номером 0 магнитную ленту, на которой был сохранен последний дамп изменений, и вводим с терминала ту же самую команду. На этом процедура восстановле- ния файловой системы заканчивается. Некоторые утверждают, что в процес- се восстановления файловой системы отдельные файлы из числа сохранен- ных на магнитной ленте можно исключить из файловой системы. Но нас этот вопрос никогда не интересовал, и мы не можем посоветовать, как это сде- лать. 235
10.5.3. РЕЗЮМЕ Надежность сохранения файловой системы — это одна из наиболее важных и ответственных задач, стоящих перед администратором ОС UNIX. Мы рас- сказали Вам о своих методах сохранения файловой системы. Безусловно, это не означает, что они хороши во всех случаях. Программы dump и restor представляют собой чрезвычайно гибкие средства в отношении типа внешних запоминающих устройств. Если надежность сохранения файловой системы очень важна для Вас, то Вам необходимо детально разобраться в процедурах сохранения и восстановления файловой системы, прежде чем приобретать новые внешние запоминающие устройства для Вашей ЭВМ. Мы всегда усту- паем требованиям тех шизофреников, что трясутся над каждым своим фай- лом, позволяя им хранить свои файлы на отдельных магнитных лентах, куда они судорожно копируют их с помощью команды tar. Кстати, в отношении файлов, в которых хранились тексты этой книги, нас самих вполне можно отнести к разряду этих типов. На практике наша ОС UNIX оказалась настолько надежной, что мы не имели до сих пор сколько-нибудь серьезных неприятностей. 10.6. СПЕЦИАЛЬНЫЕ ФАЙЛЫ "Специальные файлы? — скажете Вы.— А я то думал все файлы одинако- вые!" Действительно, при проектировании ОС UNIX одним из основных критериев ее авторов был следующий: никаких специальных файлов, любой файл — это просто файл, который может содержать все что угодно. Однако результат оказался несколько другим: ОС UNIX не налагает никаких огра- ничений на вид и организацию данных, помещенных в файл, однако специ- альные файлы, т. е. файлы некоторой специальной структуры, все-таки появились. Возможности, которые обеспечиваются в OCUNIX специаль- ными файлами, Вам не предоставит практически никакая другая операцион- ная система, а именно доступ к реальным физическим устройствам ЭВМ. В ОС UNIX поддерживается три основных типа файлов (мы сознатель- но опускаем еще один тип файлов — мультиплексные файлы, ибо они пред- ставляют собой экспериментальную конструкцию, поддерживаемую лишь в немногих версиях ОС UNIX). К первому типу файлов относятся обычные файлы, с которыми Вы уже хорошо знакомы, ко второму типу файлов от- относятся каналы, используемые для замыкан ия стандартного вывода одного процесса на стандартный ввод другого. Единственное отличие файлов перво- го типа от файлов второго типа заключаемся в том, что на размер канала налагается ограничение (чаще всего это 4096 байт) и, кроме того, файлы второго типа могут быть использованы лишь строго определенным обра- зом. Одним словом, "копаться” в канале Вам не позволено. К третьему ти- пу файлов относятся специальные файлы1. Над специальным файлом можно 1 Авторы упустили здесь из виду еще один тип файлов — каталоги; по-видимому, это объясняется тем, что все их внимание поглощено специальными файлами. (Прим, ред.) 236
производить те же операции, что и над любым другим файлом: открыть специальный файл, ввести информацию из специального файла, вывести ин- формацию в специальный файл и так далее. Однако результат применения любой из этих операций зависит от того, какому конкретному физическому устройству соответствует обрабатываемый специальный файл. Если у Вас имеется АЦПУ, то Вы, наверное, уже привыкли обращаться к нему по имени /dev/lp. На самом деле /dev/lp — это имя специального файла, соответствую- щего АЦПУ. Если обстоятельства благоприятствуют, то Вы можетеоткрыть файл /dev/lp точно так же, как любой другой обычный файл. Открытие и вывод в файл /dev/lp некоторой информации приведет к выводу ее на АЦПУ. Понятно, что попытки ввести информацию из файла /dev/lp или осуществить в нем контекстный поиск лишены всякого смысла. Этот файл может быть использован лишь строго определенным образом и "дурацкие" операции над ним не разрешены. Каждому физическому устройству должен соответствовать по крайней мере один специальный файл, хранящийся где-либо в файловой системе ОС UNIX. Чаще всего для этого используется каталог /dev. В этом каталоге хранятся специальные файлы, соответствующие терминалам, накопителям на магнитных дисках, накопителям на магнитных лентах, АЦПУ и т.д. Неко- торые из физических устройств могут использоваться любым пользователем ОС UNIX лишь в монопольном режиме, это означает, что специальный файл, соответствующий подобному физическому устройству, может быть одно- временно открыт только одним процессом. Именно таким образом исполь- зуются, например, накопитель на магнитных лентах и АЦПУ. Понятно, что попытка одновременного использования одного из этих устройств несколь- кими пользователями может привести к невероятной путанице1. Подробное описание каждого специального файла можно найти в разд.4 Руководства, который мы и призываем Вас внимательно изучить, прежде чем Вы попробуете воспользоваться одним из специальных файловОС UNIX. Использование'специальных файлов обычно очень несложно, но необходимо точное соответствие Ваших желаний возможностям тех физических уст- ройств, которым соответствуют заинтересовавшие Вас специальные файлы. Некоторые особенности отдельных физических устройств ЭВМ способны привести к тому, что использование специальных файлов, соответствующих этим физическим устройствам, станет весьма причудливым. Настойчивое экспериментальное исследование — это единственный путь получить пред- ставление обо всех особенностях использования того или иного специального 1 Подобная политика монопольного использования физических устройств ЭВМ может привести к возникновению тупиковой ситуации (см.| 1 ], с. 94 — 99). Сама ОС UNIX не предпринимает никаких усилий для устранения подобной опасности: решение этой проблемы оставлено на усмотрение пользователя ОС UNIX. Например, программа 1рг, реализующая механизм спуллинга, при выводе на АЦПУ уменьшает опасность воз- никновения тупиковых ситуаций при использовании этого физического устройства. 237
файла, ибо маловероятно, что все они будут описаны достаточно подробно в имеющейся у Вас документации. Мы, конечно же, не в состоянии привести здесь пример подобного исследования, тем более, что несовпадение устройств, имеющихся у нас и у Вас, превратит все наши попытки в пустую трату вре- мени. 10.6.1. ОСОБЕННОСТИ СПЕЦИАЛЬНЫХ ФАЙЛОВ Каждому специальному файлу соответствует один вход в каталог (обыч- но это вход в каталог /dev), содержащий имя специального файла и ссылку на описатель специального файла. И вход в каталог, и описатель специально- го файла создаются с помощью системного вызова mknod, причем правом использовать этот системный вызов обладает лишь привилегированный пользователь ОС UNIX (болеедетальное описание Вы найдете в Руководстве). Простейшим способом создания специального файла является использова- ние программы /etc/mknod, которая, естественно, использует системный вызов mknod. Программа /etc/mknod с успехом выполнит за Вас все необ- ходимые для этого действия. Каждый специальный файл обладает тремя основными признаками, которые должны быть специфицированы при его создании: является ли он блокориентированным специальным файлом, или же он является байториентированным специальным файлом; идентифика- тором класса физических устройств ЭВМ, к которому принадлежит физи- ческое устройство, соответствующее специальному файлу; идентификатором устройства в классе. Не вдаваясь в подробности, скажем, что идентифика- тор класса определяет тип физического устройства, принадлежащего этому классу. Работой любых физических устройств, принадлежащих одному и тому же классу, управляет одна и та же компонента ОС UNIX, называемая обычно драйвером устройства. Идентификатор устройства определяет кон- кретное физическое устройство в классе. Если Вы не располагаете исходны- ми текстами ядра ОС UN IX, то не сможете установить или изменить значения идентификаторов класса, так как идентификаторы классов храняться во внутренней таблице ядра ОС UNIX. В этом случае может оказаться полез- ным Руководство, но особо не рассчитывайте на него — самого интересного Вы там все равно не найдете. Если Вам известно имя интересующего Вас специального файла, то можете узнать соответствующие ему идентификатор класса и идентификатор устройства с помощью команды 1s, вызванной на выполнение с флагом —I. Таким образом, при выполнении операций ввода-вывода нужный драйвер устройства выбирается ядром ОС UNIX согласно идентификатору класса используемого специального файла, а конкретное устройство выбирается ядром ОС UNIX согласно идентификатору устройства используемого спе- циального файла. Например, наша ЭВМ имеет в своем составе три накопи- теля на магнитных дисках, каждый из них отнесен к классу с идентифика- тором класса 2. При этом накопителю с номером 0 соответствует идентифи- 238
катор устройства 0, накопителю с номером 1 — идентификатор устройства 1, а накопителю с номером 2 — идентификатор устройства 2. Единственный случай, когда необходимо знать идентификатор класса и идентификатор устройства, — это при создании соответствующего этому физическому устройству специального файла. В конечном счете лишь ядро ОС UNIX должно знать эти идентификаторы, так как с точки зрения поль- зователя ОС UNIX вполне достаточно знать имена специальных файлов. Исторически специальные файлы всегда соответствовали как физическим устройствам блочной структуры (накопители на магнитных дисках и маг- нитных лентах), так и всем прочим физическим устройствам ЭВМ (терми- налы, АЦПУ). Механизмы управления физическими устройствами различ- ной структуры всегда были и остаются до сих пор слишком различными, чтобы их можно было совместить без больших потерь в качестве. Это обсто- ятельство привело к появлению блокориентированных специальных файлов и байториентированных специальных файлов. Принятое таким образом раз- личие может показаться надуманным, если вспомнить, что большинство, если не все, физические устройства блочной структуры могут быть доступны как. физические устройства неблочной структуры. В чем же, в конце концов, разница? Всякий блокориентированный специальный файл может использоваться пользователем ОС UNIX как любой обычный файл. Так, открытие блок- ориентированного специального файла приводит к тому, что пользователь ОС UNIX получает произвольный доступ к физическому устройству, соот- ветствующему этому специальному файлу, и может производить с этим физическим устройством операции ввода-вывода; буферизация которых будет осуществляться ядром ОС UNIX. Понятно, что операция вывода ин- формации на накопитель на магнитной ленте (если, конечно, это стандартный накопитель) не может быть отнесена к операциям произвольного доступа, т.е. произвольный доступ реально, будет обеспечен только к тем устройст- вам, которые позволяют его осуществлять. , В свою очередь, открытие байториентированного специального файла приводит к тому, что пользователь ОС UNIX получает последовательный доступ к физическому устройству, соответствующему этому специальному файлу, и может производить операции ввода-вывода с этим физическим устройством, которые не будут буферизоваться ядром ОС UNIX. Любая операция ввода-вывода с физическим устройством неблочной структуры производится только.таким образом. Большинство физических устройств блочной структуры могут быть доступны как физические устройства неблочной структуры. Это означает, что этим устройствам тоже могут соот- ветствовать байториентированные специальные файлы, однако в этом случае механизм управления физическим устройством блочной структуры при осуществлении с ним операций ввода-вывода будет существенно отли- чаться от механизма управления физическим устройством неблочной струк- туры при осуществлении тех же операций с ним. В частности, любая операция 239
ввода-вывода с диском будет реализована как непосредственная передача информации между диском и оперативной памятью. При этом байты ин- формации, хранящейся в блоке диска, могут быть переданы лишь последо- вательно, начиная с первого байта, хранящегося на границе блока диска. Это означает, что операция ввода байта информации, хранящегося в середине блока диска, будет реализована как результат последовательного перебора байт информации, начиная с первого, и последующего ввода необходимого байта. Нам кажется, что для неподготовленного программиста этот факт может оказаться неожиданным. Из сказанного можно сделать очень важный вывод: передача больших массивов информации между оперативной памятью и диском будет произведена намного быстрее, если использован байториентированный специальный файл. Поясним сказанное: если для осуществления операции ввода-вывода использован байториентированный специальный файл, то передача больших объемов информации (превышаю- щих по объему один блок диска) реализуется за один обмен с диском, если же для осуществления операции ввода-вывода использован блокориенти- рованный специальный файл, то за один обмен с диском может быть реали- зована передача информации, не превышающей по объему одного блока диска. Понятно, что получить в ОС UNIX доступ к файловой системе, хранящей- ся на некотором томе, можно, открыв специальный файл, соответствующий этому тому. Поэтому если Вас беспокоят вопросы защиты информации, то Вы должны запретить рядовым (непривилегированным) пользователям доступ по чтению и доступ по записи к специальным файлам, соответствую- щим томам файловой системы, поскольку возможность доступа к этим специальным файлам означает возможность доступа к любым файлам, хра- нящимся на соответствующих им томах файловой системы (если, конечно, знать, как такой доступ осуществить). Проблема запрета доступа по чтению к специальным файлам, соответст- вующим томам файловой системы, не так проста, как кажется с первого взгляда. Это связано с тем, что, например, команда ps для получения ин- формации о процессах, находящихся в области для свопп ин га, должна иметь возможность осуществлять доступ по чтению к области для своппинга. Область для своппинга, как правило, является частью одного из дисков, на которых располагаются тома файловой системы, а это означает, что доступ к области для своппинга может быть получен лишь с помощью специаль- ного файла. Решение проблемы заключается в том, чтобы разрешить доступ по чтению к специальным файлам, соответствующим томам файловой систе- мы, привилегированному пользователю и, используя бит, обеспечивающий переустановку идентификатора пользователя в соответствии с идентифика- тором владельца файла, в котором хранится команда ps, выполнять послед- нюю от имени привилегированного пользователя. Разрешение доступа по записи к специальным файлам, соответствующим томам файловой системы, чрезвычайно опасно и может быть дано лишь выжившим из ума администратором ОС UNIX. 240
Для более детального знакомства со специальными файлами мы рекомен- дуем Вам ознакомиться со статьями из тома 26 Руководства. Возможно, что Вам даже удастся понять их. ю.6.2. команды /etc/mount и /etc/umount Любой том файловой системы может быть подключен с помощью систем- ного вызова mount и отключен с помощью системного вызова umount. Обыч- но для этой цели используют команды /etc/mount и /etc/umount соответст- венно, которые базируются на вышеупомянутых системных вызовах. Действия, выполняемые системным вызовом mount, чрезвычайно просты. Том файловой системы может быть подключен в любое место иерархичес- кой файловой системы с помощью процедуры так называемого подключе- ния тома на каталог. После выполнения процедуры подключения некоторого тома файловой системы на некоторый каталог все ссылки на этот каталог равносильны ссылкам на корневой каталог подключенного тома файловой системы. Теперь любые полные имена файлов, начинающиеся с полного имени ка- талога, на который подключен том файловой системы, станут полными именами файлов, хранящихся на подключенном томе файловой системы, а полное имя самого каталога, на который подключен том файловой систе- мы, — полным именем корневого каталога подключенного тома файловой системы. В нашей версии ОС UNIX ввод с терминала команды /etc/mount /dev/rf 1 /usr/tmp приводит к тому, что абсолютное полное имя файла /usr/tmp становится аб- солютным полным именем корневого каталога файловой системы, храня- щейся на диске, которому соответствует специальный файл /dev/rf 1. Для по- лучения доступа к некоторому файду, хранящемуся на подключенном томе файловой системы, необходимо, чтобы его абсолютное полное имя начиналось с /usr/tmp. Процедура отключения тома файловой системы также проста, однако ОС UNIX не позволит Вам отключить том файловой системы до тех пор, пока все файлы, открытые пользователями ОС UNIX и хранящиеся на подключен- ном томе файловой системы, не будут закрыты. В результате выполнения команд /etc/mount и /etc/umount изменяется содержимое файла /etc/mtab, представляющее собой таблицу имен специальных файлов, соответствующих тем физическим устройствам ЭВМ, на которых установлены подключенные тома файловой системы. Любые жалобы команд /etc/mount и /etc/umount на содержимое указанной таблицы можно смело игнорировать. Информация, содержащаяся в этой таблице, чаще всего не соответствует истинному поло- 241
жению дел, особенно после краха ОС UNIX1, и представляет для Вас чисто абстрактный интерес. Будьте чрезвычайно осторожны при подключении тома файловой системы на каталог, содержащий какие-либо имена файлов кроме, и.. . Если Вы все-таки подключили том файловой системы на каталог, содержащий какие- либо входы кроме двух указанных выше, то все файлы, соответствующие этим входам, будут недоступны Вам до тех пор, пока Вы не отключите под- ключенный ранее том файловой системы, ибо полное имя этого каталога "узурпируется" подключенным томом файловой системы для обозначения собственного корневого каталога. Изучите описание команд /etc/mount и /etc/иmount, приведенное в Руководстве, при этом особое внимание обратите на флаг —г, специфицирующий такой режим выполнения этих команд, при котором доступ по записи к файлам подключенного тома файловой систе- мы запрещен. Если входящий в состав ЭВМ накопитель на магнитной ленте не обеспечивает возможности произвольного доступа, то при подключении файловых систем, хранящихся на магнитной ленте, флаг -г должен исполь- зоваться непременно, так как в противном случае содержимое магнитной ленты будет разрушено. 10.7. "ДОМАШНЕЕ ХОЗЯЙСТВО" OCUNIX Совсем недавно мы упоминали о программах cron и update. На самом деле эти программы хранятся в файлах /etc/cron и /etc/update соответственно. Поэтому корректнее было бы называть их /etc/cron и /etc/update. Выполнение обеих этих программ инициируется процессом, в рамках которого выполня- ется программа init, передающим при переводе ОС UNIX в многопользова- тельский режим интерпретатору команд shell на выполнение командный файл /etc/rc, содержащий, как правило, те команды ОС UNIX, которые необ- ходимо выполнить в процессе запуска ОС UNIX. Программа update очень проста; единственной ее задачей является издание каждые 30 с системного вызова sync, что обеспечивает своевременное обнов- ление информации на диске и тем самым спасает нас от катастрофы при вне- запном останове ЭВМ. Программа, cron функционально более сложна. Вызванная один раз на выполнение, она, сверяясь ежеминутно с информацией, содержащейся в файле /usr/Jib/crontab, вызывает (или нет) на выполнение указанные ей команды ОС UNIX. Этот механизм весьма удобен для автоматического выполнения таких работ, как восстановление файловой системы, сбор статистической ин- формации, периодический вывод сообщений в канал связи с другой ЭВМ и многих других. Как специфицировать необходимые Вам конкретные работы, 1 Под крахом ОС UNIX понимается ситуация, когда последняя неожиданно перестает функционировать в результате неполадок в аппаратуре ЭВМ или программном обеспече- нии. (Прим, ред.) 242
можно узнать, изучив Руководство. Мы же обратим Ваше внимание лишь на тот факт, что большинство неискушенных пользователей используют для этой цели команду at (описание которой Вы найдете в at (1)), которая и выполня- ет за них всю "грязную работу". Лично мы используем программу cron для установки разрешения доступа по исполнению к файлам, содержащим игровые программы, лишь в определен- ное время суток и на определенный интервал времени. Кроме этого, мы ис- пользуем программу cron для удаления "старых" файлов (а старыми мы считаем файлы, доступ к которым не осуществлялся в течение двух дней), постоянно накапливающихся в каталоге /tmp. При функционировании ОС UNIX программа cron выполняет те работы, которые в противном случае Вы были бы вынуждены выполнять сами, например контроль соблюдения расписа- ния машинного времени. Способная вызывать на выполнение в определенные моменты времени команды ОС UNIX из определенного набора программа cron может существенно облегчить и без того рутинный труд администратора ОС UNIX. 10.7.1. КОМАНДА 1рг Команда 1рг используется для вывода текста на АЦПУ. По вполне понят- ным причинам пользователи ОС UNIX не имеют возможности использовать АЦПУ одновременно, поэтому во избежание конфликтных ситуаций между пользователями ОС UNIX им предлагается использовать виртуальное АЦПУ, которое эмулируется командой 1рг. Если команда 1рг вызвана на выполнение с аргументами, которые являются именами файлов, то содержимое указан- ных файлов будет выведено на АЦПУ, в противном случае команда 1рг будет копировать на АЦПУ свой стандартный ввод. Как и большинство команд ОС UNIX, команда 1рг имеет целый ряд режимов работы, каждый из которых может быть специфицирован соответствующим ему флагом. Описание всех флагов команды 1рг Вы найдете в 1рг (1). Для файлов, имена которых указаны в списке аргументов команды 1рг, последняя организует очередь вывода на АЦПУ, для чего в каталоге /usr/spool/lpd создаются соответствующие входы. Фактический вывод содержимого файлов на АЦПУ производится программой /etc/lpd, которую иногда называют спулером. Ее задачи весьма просты — эта программа просматривает содержимое каталога/usr/spool/lpd и, обнаружив в нем имя некоторого файла, выводит его содержимое на АЦПУ, после чего удаляет соответствующий вход в каталог /usr/spool/lpd. Если при этом оказывается, что каталог /usr/spool/lpd пуст, то программа/etc/lpd заверша- ет свою работу. Вызов на выполнение программы /etc/lpd производится командой Ipd каждый раз, когда последняя вызывается на выполнение. Про- цесс, в рамках которого выполняется программа /etc/lpd,—это единственный процесс, который имеет право доступа по записи к специальному фалу, соот- ветствующему АЦПУ. Таким образом, в обычной ситуации рядовой пользо- ватель ОС UNIX не имеет непосредственного доступа к АЦПУ. Понятно, что при таком механизме спуллинга существует принципиальная 243
возможность, когда два или более процесса, в рамках которых выполняется программа /etc/lpd, попытаются одновременно открыть специальный файл, соответствующий АЦПУ, Во избежание этого первый из порожденных процес- сов, в рамках которого выполняется программа /etc/lpd, создает файл /usr/ /spool/Ipd/tock, который будет удален этим процессом только непосредст- венно перед его завершением. Каждый вновь порожденный процесс, в рам- ках которого выполняется программа /etc/lpd, перед началом вывода ин- формации на АЦПУ проверяет, существует ли файл /usr/spool/lpd/iock, и ес- ли он существует, то процесс немедленно завершается, в противном случае он создает файл /usr/spool/lpd/iock, производит вывод на АЦПУ, а затем уда- ляет указанный файл. Подобный метод синхронизации процессов не идеален и имеет один скрытый дефект, поэтому для полного решения этой пробле- мы необходимо привлечение механизма семафоров {см. [1], с. 16 — 23). Мы не станем рассматривать этот скрытый дефект, так нак описанный метод синхронизации процессов имеет еще один гораздо более существенный недос- таток. Представим себе, что по какой-то причине процесс, в рамках которого выполняется программа /etc/lpd, завершился, не удалив предварительно фай- ла /usr/spool/lpd/iock (такой причиной может быть внезапно возникшая не- исправность ЭВМ или крах ОС UNIX), в таком случае всякий вновь порож- денный процесс, в рамках которого выполняется программа /etc/lpd, убедив- шись в существовании указанного файла, немедленно завершится. Внешне такая ситуация выглядит следующим образом: АЦПУ внезапно прекращает работу, и попытки повторить вывод содержимого некоторого файла на него ни к чему не приводят. Чтобы разобраться в том, что же произошло, вос- пользуйтесь командой ps, описание которой Вы найдете в ps(1). Если окажет- ся, что процесс, в рамках которого выполняется программа /etc/lpd, еще не завершился, то это означает, что АЦПУ вышло из строя, а сам процесс нахо- дится в состоянии ожидания готовности его к дальнейшей работе. Если же окажется, что процесс, в рамках которого выполняется программа /etc/lpd, завершился и к тому же файл /usr/spool/lpd/iock существует, удалите указан- ный файл, и первый же вызов на выполнение команды 1рг приведет к возоб- новлению вывода на АЦПУ. 10.8. НОВЫЕ ПОЛЬЗОВАТЕЛИ Неожиданным пробелом в стандартной версии 7 ОС UNIX является от- сутствие в ней каких-либо средств регистрации новых пользователей ОС UNIX и исключения пользователей ОС UNIX, отказавшихся от ее услуг. Правда, в большинстве версий ОС UNIX такие средства все-таки имеются, но все они настолько различны, что мы лучше расскажем Вам, как выполнить эти опера- ции "вручную". И, даже если используемая Вами версия ОС UNIX содержит средства автоматической регистрации и исключения пользователей, Вам мо- жет показаться интересным узнать, какие действия они выполняют. Регистрация нового пользователя ОС UNIX представляет собой процедуру, состоящую из трех шагов. На первом шаге Вы должны модифицировать файл. 244
содержащий досье на всех пользователей ОС UNIX (в частности, имена и паро- ли пользователей ОС UNIX), на втором шаге модифицировать файл, содержа- щий имена и пароли групп пользователей ОС UNIX, и, наконец, на третьем шаге создать для нового пользователя ОС UNIX основной каталог пользовате- ля. Для того чтобы исключить пользователя ОС UNIX, необходимо выполнить эту процедуру в обратном порядке. Предположим, что Вы хотите зарегистрировать нового пользователя ОС UNIX и решили дать ему (а точнее ей) входное имя eva. Прежде всего, необ- ходимо убедиться в том, что среди уже зарегистрированных пользователей ОС UNIX нет обладательницы такого же входного имени. Для этого Вам не- обходимо заглянуть в файл /etc/passwd. Это очень просто сделать с помощью команды grep eva /etc/passwd В результате выполнения этой команды на терминал будут выведены все строки, содержащие контекст eva. Каждая строка файла /etc/passwd имеет следующий формат: xy:xxxxxx:7:5::/usr/xyz: Как видите, она содержит несколько полей, разделенных одним или несколь- кими символама :. Приведенная здесь строка файла /etc/passwd содержит регистрационную информацию о пользователе ОС UNIX входное имя кото- рого xyz, пароль после соответствующего кодирования имеет вид хххххх, идентификатор пользователя и идентификатор группы 7 и 5 соответственно и основной каталог пользователя /usr/xyz. Подробное описание содержимого строки файла /etc/passwd Вы найдете в разд. 5 Руководства. Прочитав его, Вы сможете подробнее познакомиться с назначением каждого поля в строках файла /etc/passwd. Теперь, ориентируясь на содержимое уже имеющихся в файле /etc/passwd строк, введите в него еще одну строку — строку с регистрационной инфор- мацией о новом пользователе ОС UNIX. Выбирая для нового пользователя ОС UNIX идентификатор пользователя и идентификатор группы, не забывайте, что первый должен быть уникальным, а второй должен отвечать тому крите- рию, которым Вы пользуетесь, объединяя пользователей ОС UNIX в группы. Проделать все эти операции можно с помощью редактора текстов ed при ус- ловии, что Вььобладаете при этом полномочиями привилегированного поль- зователя ОС UNIX. Оставьте поле для закодированного пароля пользователя ОС UNIX пустым и получите в итоге строку примерно следующего вида: eva::22:5::/usr/eva: Итак, Вы выполнили свою работу. Отредактировав должным образом файл /etc/passwd можете приступать к редактированию файла /etc/group, чтобы указать ОС UNIX на то, что в группе, к которой Вы отнесли нового пользователя ОС UNIX, появился новый член. 245
Эта операция может быть выполнена добавлением контекста ,eva (не потеряй- те символ , ) в строке файла /etc/group, содержащей список входных имен членов группы, к которой Вы отнесли и нового пользователя ОС UNIX. Файл 7etc/group кроме прочей информации содержит информацию о том, в составе каких групп имеет право работать пользователь ОС UNIX, имеющий данное входное имя, поэтому если Вы предполагаете предоставить новому пользо- вателю ОС UNIX право работать в составе различных групп, то добавьте кон- текст ,eva во все те строки файла /etc/group, которые содержат информацию о группах, в которых имеет право работать новый пользователь ОС UNIX. Как это не странно, но мы предостерегаем Вас от заведения паролей групп, ибо хотя такая возможность есть, но воспользоваться ей, как нам кажется, очень непросто. Дело в том, что команды для заведения или изменения пароля группы (аналогичной команде passwd,c помощью которой может быть заведен или изменен пароль пользователя) в стандартной версии? ОС UNIX не сущест- вует. Если Вы сомневаетесь, к какой группе отнести нового пользователя ОС UNIX, вспомните, что большинство пользователей ОС UNIX отнесены к груп- пе под названием "прочие", отнесите к ней для начала и нового пользователя ОС UNIX. На последнем шаге Вам предстоит создать для нового пользователя его ос- новной каталог пользователя, для чего необходимо воспользоваться коман- дой mkdir. Не забудьте изменить имя владельца созданного каталога на вход- ное имя нового пользователя, иначе он не сможет создать в своем основном каталоге пользователя ни одного файла. Для исключения пользователя ОС UNIX, как мы уже говорили, необходи- мо повторить все шаги процедуры регистрации нового пользователя ОС UNIX в обратном порядке. Единственное, что здесь добавится, — это трудная рабо- та по удалению всех файлов, владелец которых исключен из ОС UNIX. Для выполнения этой работы войдите в ОС UNIX в качестве привилегированного пользователя, затем с помощью команды cd сделайте текущим каталогом ос- новной каталог пользователя, владелец которого должен быть исключен, и введите с терминала команду rm — rf * В результате выполнения приведенной выше команды будут удалены все обычные файлы и все каталоги, расположенные на дереве файловой системы ниже текущего каталога. После этого с помощью все той же команды cd установите в качестве текущего любой другой каталог, с помощью команды rmdir удалите основной каталог пользователя, владельцем которого был бывший пользователь ОС UNIX. Эта последняя операция является не обяза- тельной, но весьма полезной предосторожностью. Если Вы вызовите на вы- полнение команду, удаляющую "все и вся ", находясь не там, где надо, то попадете в чрезвычайно затруднительное положение. В кажущейся столь очевидной процедуре редактирования файла /etc/ /passwd существует один тонкий момент, связанный с использованием команды passwd. Команда passwd также редактирует файл /etc/passwd, и у 246
нас есть основания подозревать, что два процесса, в рамках которых выпол- няется команда passwd, выполняющиеся одновременно, могут неблагоприят- но повлиять на работу друг друга, хотя мы и не проверяли этого до конца. Если здесь действительно существует некая проблема, мы не думаем, что она будет возникать очень часто. Единственный шанс получить невообразимое содержимое в файле /etc/passwd — это умудриться заставить оба процесса, в рамках которых выполняется команда passwd, произвести вывод в файл /etc/ /passwd одновременно. ПРИЛОЖЕНИЕ А. КОМАНДЫ ОБЩЕГО НАЗНАЧЕНИЯ Большинство версий ОС UNIX предоставляют своим пользователям весь- ма обширный набор команд общего назначения. Мы перечислим здесь боль- шинство из этих команд, вошедших в состав стандартной версии 7 ОС UNIX. Более подробное описание этих команд Вы сможете найти в разд. 1 Руковод- ства, а если Вам повезет, то в разд. 6 Руководства Вы найдете описания ко- манд, вошедших в состав лишь конкретно вашей версии ОС UNIX. Синтаксис приводимых здесь описаний почти полностью совпадает с син- таксисом описаний, принятым в Руководстве. Любой объект, имя которого заключено между символом [ и символом ] , представляет собой необяза- тельную часть команды. Если же между указанными символами заключена последовательность некоторых символов и известно, что это именно после- довательность отдельных символов, а не имя некоторого объекта, то при ис- пользовании данной команды Вы можете воспользоваться произвольным фрагментом этой последовательности символов. Далее, "многоточие", т.е. контекст вида ..., указывает на то, что на его месте может быть указано произвольное число имен некоторых объектов. Выделение какого-либо кон- текста полужирным шрифтом говорит о том, что данный контекст представ- ляет собой ключевое слово, которое должно вводиться с терминала в точ- ности в таком виде, в каком оно указано в описании. adb — команда вызова на выполнение отладчика adb [- w] [objfil] [corfil] — w = открыть файлы objfil и corfil для осуществления операций ввода- вывода; в случае отсутствия имен файлов в списке аргументов использовать файлы a. out и core соответственно (при необходимости эти файлы будут созданы). аг — команда создания и обработки архивного файла аг [dmpqrtx] [abciluv] [posname] archive file .. . d = удалить из архивного файла модули с именами file...; m = переместить модуль с именем file в конец архивного файла или в не- которое положение относительно модуля с именем posname; р = вывести на терминал содержимое модулей архивного файла, специфи- цированных именами file ...; 247
q = добавить в конец архивного файла модули с именами fi1е . . . (провер- ка существования в архивном файле модулей с такими именами не произво- дится) ; г = осуществить замещение или добавление в архивный файл новых моду- лей; t = вывести на терминал список имен модулей, содержащихся в архивном файле archive; х = извлечь из архивного файла archive модули с именами file ... и раз- местить каждый из них в отдельном файле, имя которого должно совпадать с именем соответствующего модуля архивного файла; а = после модуля с именем posname; b = перед модулем с именем posname; с = запретить вывод на терминал сообщения о создании файла archive; i = перед модулем с именем posname; 1 = для создания временного файла использовать текущий каталог вместо каталога /tmp; и = при осуществлении замещения модулей архивного файла обрабатывать лишь те модули, даты последней модификации которых устарели; v = выводить на терминал сообщение по каждой производимой командой операции. as — команда вызова на выполнение транслятора с языка Ассемблера as [-] [-о objfile] file ... — = все неудовлетворенные ссылки объявить внешними; —о = использовать следующий аргумент в качестве имени выходного фай- ла (по умолчанию используется файл a. out). at — команда вызова на выполнение программы в указанное астрономи- ческое время at time[apnm] [day] [week] [file] time = четырехзначное число, специфицирующее время в часах и минутах; а =до 12:00; р = после 12:00; п = полдень; m = полночь; day = или последовательность названия месяца и номера дня, или название дня недели; week= отложить на неделю действия, вызываемые командой; file = имя файла, содержащего вызываемую на выполнение команду (про- грамму) (по умолчанию используется стандартный ввод). awk — команда осуществления контекстного поиска и преобразования текста awk [-Fc] [-f progfile] [prog] [file] ... — Fc = использовать символ с в качестве разделителя полей; — f = использовать следующий аргумент в качестве имени файла, содержа- 248
щего исходный текст программы на языке программирования awk (по умол- чанию используется файл prog). bas — команда вызова на выполнение интерпретатора с языка програм- мирования Бейсик bas [file] file = имя файла, содержащего исходный текст программы. basename - команда удаления отдельных слогов или окончания имени файла basename string [suffix] string = полное имя файла, из которого будут удалены все слоги, кроме последнего; suffix = окончание, удаляемое из имени файла в случае существования такого окончания. be — команда вызова на выполнение транслятора с языка программирова- ния, ориентированного на осуществление арифметических операций неогра- ниченной точности be [-1] [-с] [file ...] — 1 = использовать библиотеку функций; — с = произвести лишь трансляцию программы; f i le ... = имена файлов, содержащих исходный текст программы. cal — команда вывода на стандартный вывод календаря cal [month] year month = порядковый номер интересующего Вас месяца года (целое деся- тичное число от 1 до 12) ; year = порядковый номер интересующего Вас года (целое десятичное чис- ло от 1 до 9999). calendar — команда осуществления своевременных напоминаний calendar[—] = послать всем пользователям предназначенные им напоми- нания. cat — команда объединения и вывода на стандартный вывод содержимого нескольких файлов cat [-u] file ... — и = организовать небуферизованный вывод. cb — команда организации структурированного листинга программы, на- писанной на языке программирования Си cb сс — команда вызова на выполнение транслятора с языка программирова- ния Си сс [-с] [-р] [-f] [-0] [-S] [-Р] [-о output] [-Dname = def]][-Uname] [ —Idir] file.c ... [-Im] [ofile...] — с = осуществить лишь трансляцию содержимого файлов f i le.c ..., объект- 249
ный код вывести в файлы file.o . . . соответственно; — р = обеспечить получение профиля времени выполнения программы и вывести полученную информацию в файл mon.out; — f = использовать интерпретатор арифметических операций с плавающей точкой; — 0= оптимизировать объектный код; — S= осуществить трансляцию содержимого файлов f i le.c... лишь до эта- па ассемблирования, результат вывести в файлы file.s ... соответственно; — Р = вызвать на выполнение лишь препроцессор; — о = вывести результат трансляции в файл output (по умолчанию исполь- зуется файл a. out в текущем каталоге); — Dname = def = задать определение макроподстановки переменной пате для препроцессора; — Dname = присвоить переменной пате значение 1; — Uname = игнорировать все определения макропостановок переменной name для препроцессора; — Idir = использовать каталог dir для поиска включаемых файлов1; - 1m = передать команде Id флаг -Im; ofile ... = имена объектных файлов, компонуемых должным образом с объектными файлами, полученными в результате трансляции. cd — команда смены текущего каталога cd directory directory = имя каталога, устанавливаемого в качестве текущего каталога. chgrp — команда смены идентификатора группы файлов2 chgrp group file ... group = или десятичное число - идентификатор группы пользователей, или групповое имя; file ... = имена файлов, идентификатор группы которых будет сменен. chmod — команда изменения режима доступа к файлам3 chmod mode file ... mode — режим доступа к файлам, заданный или в символьном виде [ugoa] [+ — =] [rwxct], или в виде восьмеричного числа (см. гл. 5). file ... — имена файлов, код защиты файла которых будет сменен. chown _ команда смены идентификатора владельца файлов* * chown owner file ... 1 Поиск включаемых файлов начинается с текущего каталога, продолжается в ката- логе dir, а затем осуществляется обычным порядком. (Прим, ред.) Команда chgrp может быть применена только привилегированным пользователем ОС UNIX или владельцем файла. (Прим, ред.) * Команда chown может быть применена только привилегированным пользователем ОС UNIX ил и владельцем файла. (Прим, ред.) 250
owner = или десятичное число — идентификатор пользователя, или вход- ное имя; file ... = имена файлов, идентификатор владельца которых будет сменен. стр - команда сравнения содержимого двух файлов стр [-1] [-s] filel file2 —1 = вывести на стандартный вывод десятичные порядковые номера не- совпадающих байт и сами эти байты в восьмеричном виде; —s = не выводить никакой информации на стандартный вывод, установить в качестве возвращаемого значения значение кода завершения: 0 — полное совпадение; 1 — несовпадение содержимого файлов; 2 — аргументы коман- ды заданы неверно. col — команда управления построчным выводом файла col [ — bfх] — b = запретить вывод последовательности кодов ASCII вида ESC,10; — f = разрешить вывод последовательности кодов ASCII вида ESC.8; — х = запретит^ интерпретацию последовательности символов ш как по- следовательности символов tab. comm — команда построчного сравнения двух текстовых файлов comm [ — 123] filel file2 — 1 = запретить вывод строк, содержащихся только в файле filel; — 2 = запретить вывод строк, содержащихся только в файле f i Ie2; — 3 = запретить вывод строк, содержащихся в обоих файлах. ср — команда копирования файлов ср oldfile newfile ср file ... directory newfile = имя файла, в который копируется содержимое файла oldfile; directory = имя каталога, содержащего отныне имена файлов file .. . . crypt — команда шифровки/дешифровки текста crypt [password] password = ключ шифра. date — команда установки даты или вывода даты на стандартный вывод date [yymmddhhmm] I [.ss] УУ = год (две цифры) ; mm = месяц (две цифры); dd =день (две цифры) ; уу = час (две цифры); mm = минуты (две цифры); ss = секунды (две цифры); установка даты может быть осуществлена только привилегированным поль- зователем ОС UNIX. de — команда осуществления простых арифметических вычислений с произ- вольной точностью 251
de [file] fi le = имя файла, содержащего необходимую последовательность арифме- тических операций. dd — команда преобразования и копирования файлов dd [option = value] ... option value if = имя входного файла; of = имя выходного файла; ibs = размер блока внешнего запоминающего устройства (в байтах), на котором хранится входной файл (по умолчанию 512); obs = размер блока внешнего запоминающего устройства (в байтах), на котором будет храниться выходной файл (по умолчанию 512); bs = размер блока внешних запоминающих устройств, между которыми осуществляется обмен (в байтах); ebs = размер буфера (в байтах), используемого для осуществления необ- ходимого преобразования; flies = число входных файлов, которые должны быть скопированы1 ; skip = число записей, опускаемых при осуществлении операции копирова- ния, на внешнем запоминающем устройстве, на котором хранятся входные файлы; seek = число записей на внешнем запоминающем устройстве, на котором хранятся выходные файлы, пропускаемые перед осуществлением операции копирования; count = количество записей на внешнем устройстве, на котором хранятся входные файлы, участвующие в операции копирования ; conv = ascii, ebcdic, ibm, lease, ucase, swab, sync, noerror deroff — команда удаления из содержимого текстового файла директив форматирования текста deroff [- w] file ... — w = выводить на стандартный вывод лишь по одному слову из каждой обработанной строки текста. diff — команда сопоставления различий между содержимым двух файлов diff [-befh] filel file2 — b = игнорировать любую последовательность символов tab и и, если она находится в конце файла; — е = вывести на стандартный вывод последовательность команд редактора текстов ed, позволяющую преобразовать содержимое файла filel таким об- разом, чтобы оно совпадало с содержимым файла file2; 1 В Руководстве files задает число входных файлов, опускаемых при осуществлении операции копирования. (Прим, ред.} 252
— f = вывести на стандартный вы вод последовательность команд редактора текстов ed, позволяющую преобразовать содержимое файла file2 таким об- разом, чтобы оно совпадало с содержимым файла filel; — h = быстро и поверхностно сравнить содержимое двух файлов произ- вольной длины1. diff3 — команда сопоставления различий между содержимым трех файлов diff3 [-ехЗ] filel file2 file3 — е = вывести на стандартный вывод последовательность команд редактора текстов ed, позволяющую добавить к содержимому файла filel отличия со- держимого файла f i 1еЗ от содержимого файла file2; — х = вывести на стандартный вывод последовательность команд редакто- ра текстов ed, позволяющую добавить к содержимому файла filel отличия содержимого всех трех файлов друг от друга; — 3 = вывести на стандартный вывод последовательность команд редакто- ра текстов ed, юзволяющую добавить к содержимому файла filel отличия содержимого файла fi 1еЗ от содержимого файла filel. du — команда определения суммарной степени использования диска du [-а] [ — s] [file ...] — а = вывести на стандартный вывод число блоков диска, занимаемых каждым из файлов, находящихся на этом диске; — s = вывести на стандартный вывод общее число блоков диска, занима- емых всеми файлами, находящимися на этом диске. echo — команда вывода на стандартный вывод ее собственных аргументов echo [ — n] [arg ...] — п = запретить вывод на стандартный вывод символа newline. ed — команда вызова на выполнение редактора текстов ed ed [-] [-х] [file] — = запретить вывод на стандартный вывод числа байт, содержащихся в файле fi 1е; — х = эмулировать команду х редактора текстов ed сразу после вызова по- следнего на выполнение. eqn — команда, представляющая собой препроцессор программы troff для представления специальных математических выражений eqn [ — dxy] [ — fn] [- pn] [-sn] [file] ... — dxy = использовать символы x и у в качестве ограничителей обрабаты- ваемого фрагмента текста; — fn = установить номер используемого шрифта равным п; 1 Использование флага —h исключает возможность использования флагов —е и —£ (Прим, ред.) 253
— pn = установить размер нижнего {верхнего) индекса равным и пунктам1 ; — sn — установить размер символа равным п пунктам, ехрг — команда вывода на стандартный вывод ее собственных аргументов, вычисленных как арифметические выражения ехрг arg ... f77 — команда вызова на выполнение транслятора с языка программиро- вания Фортран 77 f77 [ —с] [-р] [-0] [-f] [-о output] [-onetrip] [-u] [-C] [ —w] [-w66] [-F] [-m] [-Ex] [ —Rx] fiie.f ...[-Im] ofile ... — с = осуществить лишь трансляцию входных файлов fiie.f .... объектный код вывести в файлы fi le.o ... соответственно; — р = осуществить профилирование времени выполнения программ и вы- вести полученную информацию в файл mon.out; — О = оптимизировать объектный код; — S = осуществить трансляцию содержимого входных файлов fiie.f... лишь до этапа ассемблирования, результаты вывести в файлы file.s ... соот- ветственно; — о output = вывести результат трансляции в файл output (по умолчанию используется файл a. out); — onetrip = транслировать лишь те операторы циклов, тела которых будут выполнены хоть один раз; — и — запретить использование правил определения типов переменных по умолчанию и отнести все определенные таким образом переменные к типу undefined; — С = генерировать объектный код, осуществляющий проверку попадания индекса массива в границы, заданные при определении этого массива; — w = запретить вывод на стандартный вывод всех предупреждающих со- общений; - w66 = запретить вывод на стандартный вывод всех предупреждающих сообщений, сгенерированных в результате обработки содержимого входных файлов как исходных текстов программ, написанных на языке Фортран 66; — F= обработать содержимое входных файлов f i le.e ... с помощью препро- цессора EFL1 2, а содержимое входных файлов fi le.r... с помощью препроцес- сора RATFOR3, результаты поместить в файлы fiie.f ... соответственно; — m = обработать содержимое входных файлов с помощью препроцессора М4; 1 Основная единица длины в типа метрической системе — типографский пункт (л.), равный примерно 0,376 мм. (Прим, ред.) 2 EFL — Extended Fortran Language. (Прим, ред.) 3 RATFOR - RATional FORtran. (Прим, ред.) 254
— Ex — использовать строку х в качестве набора аргументов препроцессора EFL; — Rx = использовать строку х в качестве набора аргументов препроцессора RATFOR; — 1m — передать команде Id флаг —1m. factor — команда разложения целого числа на взаимно простые сомножи- тели factor [number] file — команда определения типа файла file file find — команда поиска файлов find pathname expression pathname = имя каталога, с которого должен быть начат поиск файлов1; expression = булевское выражение, состоящее из некоторых перечислен- ных ниже компонент; п — целое десятичное число; запись вида п соответ- ствует условию - п, запись вида +п соответствует условию >г), а запись вида —л — условию <п, компонента булевского выражения принимает значение "истина", если: -name filename имя файла filename совпадает с именем выбранного файла; —perm onum тип доступа к выбранному файлу совпадает с типом доступа к файлу, специфицированным восьмеричным числом опит; —type x тип найденного файла совпадает с типом файла, специ- фицированным символом х, здесь в качестве символа х могут быть использованы символы b, с, d и f* 2 -links n —user uname имеется п ссылок на выбранный файл; выбранный файл принадлежит пользователю, имеюще- му входное имя uname; — group g name выбранный файл принадлежит группе пользователей, имеющей групповое имя gname; —size n —inum n выбранный файл занимает п блоков диска; выбранному файлу соответствует описатель файла номер п; ’ Согласно Руководству поиск будет производиться лишь в каталоге pathname, а на месте имени pathname может быть указан список имен таких каталогов {Прим, ред.] 2 Напомним читателю типы файлов: Ь —блокориентированныйспециальный файл; с — байториентированный специальный файл; d — каталог; f — обычный файл. {Прим, ред.) 255
—atime п доступ к выбранному файлу был осуществлен п дней назад; —mtime п —ехес command выбранный файл был модифицирован п дней назад; команда command возвращает в качестве кода завер- шения нуль; —ok command команда command в качестве кода завершения возвра- щает нуль и со стандартного ввода вводится подтверж- дение в виде символа у; —print всегда истинно, указывает на необходимость вывода на стандартный вывод имени текущего каталога; —newer nfiie файл nfile был модифицирован раньше, чем выбран- ный файл. Указанные компоненты могут быть сгруппированы в булевское выражение с помощью следующих логических операций: ! — логическое отрицание; —а — логическое И; —о — логическое ИЛИ; \ ( \) — для указания последовательности выполнения логических операций. graph — команда вывода на стандартный вывод графической информации graph option ... options — а = автоматическое задание абсциссы очередной точки кривой; — b = осуществление разрыва кривой после каждого ввода со стандарт- ного ввода строки символов, помечающей точку кривой; — g п = число п специфицирует тип разметки: 0 — вывод только неразме- ченной кривой, 1 — вывод размеченных осей и неразмеченной кривой, 2 — полная разметка; — с с = строка символов с используется для пометок всех точек кривой; — m п = п-режим вывода кривой: 0 —вывод кривой в виде последователь- ности точек, 1 — вывод кривой в виде ломаной линии (по умолчанию исполь- зуется режим вывода 1); — 1 lab = пометить кривую контекстом lab; — s = вывод кривой на терминал и сохранение изображения на экране тер- минала в случае замыкания стандартного вывода команды graph на стандарт- ный ввод команды plot; — п f = f-относительная величина высоты прямоугольного фрагмента листа для вывода графика кривой; — w f e f-относительная величина ширины прямоугольного фрагмента лис- та для вывода графика кривой; — г f = f-относительная величина сдвига вправо перед выводом кривой; — u f = f-относительная величина сдвига вверх перед выводом кривой; — t = поменять местами оси ординат; — х[|] = следующие три аргумента представляют собой соответственно: значение левой границы по оси абсцисс, значение правой границы по оси 256
абсцисс и масштаб оси абсцисс; I соответствует логарифмическому масштабу; — У [I] = следующие три аргумента представляют собой соответственно значение нижней границы по оси ординат, значение верхней границы по оси ординат и масштаб оси ординат; I соответствует логарифмическому масштабу. grep — команда поиска некоторого контекста в содержимом текстового файла grep [option] ... ехрг [file] egrep [option] ... [ехрг] [file] fgrep [option] ... [strings] [file] options — b = вывод номеров блоков диска; — с = вывести только число вхождений контекста в содержимое файла; — е ехрг = использовать контекст ехрг для осуществления контекстного поиска1; — f file = осуществить поиск контекста, находящегося в файле file;. — h = запретить вывод в качестве заголовков имен файлов, содержащих искомый контекст; — 1 = вывести на стандартный вывод список имен файлов, содержащих за- данный контекст; — п = вывести на стандартный вывод номера строк, содержащих заданный контекст; — s = запретить вывод какой бы то ни было информации на стандартный вывод, в качестве значения, возвращаемого командой, использовать значение кода завершения; — V = вывести на стандартный вывод все строки, содержащие заданный контекст; — х = вывести на стандартный вывод строки, целиком состоящие из задан- ного контекста; —у = запретить различение строчных и прописных букв при осуществлении контекстного поиска. join — команда управления реляционной базой данных join[option] ...filel file2 options n = число 1 или 2; — an = дополнительно к обычному выводу осуществить вывод непарных строк в файл filen; — es= использовать строкуэ для замены символов tab и >_> ее содержимым; — jnm = использовать в качестве первичного ключа поле номера m файла filen; — о n.m = вывести на стандартный вывод поле номера m файла fi len; — tc=^ использовать символ с в качестве разделителя полей. 1 Полезно в том случае, если контекст ехрг начинается с символа —. (Прим, ред.)- 9 Зак 1165 257
kill — команда аварийного завершения выполнения процесса kill [ - signo] processid processid = номер процесса, которому посылается специфицированный сигнал; signo; 1 — отмена (SIGHUP); 2 = прерывание (SIGINT); 3 = нестандартный выход (SIGQUIT); 4 = неверная команда (SIGILL); 5=ловушка (SIGTRAP); 6= ЮТ (SIGIOT) только для PDP-11; 7=ЕМТ (SIGEMT) только для PDP-11; 8 = исключительная ситуация при выполнении операций с плавающей точ- кой (SIGFPE); 9 = уничтожение процесса (SIGKILL); 10 = ошибка шины (SIGBUS); 11 = нарушение сегментации (SIGSEGV); 12 = неверный системный вызов (SIGSYS); 13 = запись в канал без чтения из него (SIGPIPE) ; 14 = будильник (SIGALRM); 15 = программное завершение процесса (SIGTERM); 16 = резервный сигнал. Id — команда вызова на выполнение компоновщика Id [-suIxXrdnioeOD] file ... — s ~ запретить вывод таблицы символов и значений битов перемещений; — u name = ввести в таблицу символов name в качестве символа типа undefined; - 1х = использовать при компоновке библиотеку, находящуюся в файле /lib/libx.a; здесь х — это строка символов1; — х = запретить занесение в таблицу символов имен локальных переменных; — Х= занести в таблицу символов имена локальных переменных, за исклю- чением тех из них, которые начинаются с символа L; — г = вывести сгенерированные значения битов перемещений; — d = задать принудительное определение общей области памяти; — п = генерировать объектный код, позволяющий разделять его между раз- личными процессами; — i = генерировать объектный код с разделенными областями инструкций и данных; — о output = вывести результат компоновки в файл output вместо файла a.out; — е symbol = использовать имя symbol в качестве имени точки входа (по 1 Если файл с таким именем не существует, то поиск производится в файле usr/lib/libx.a {Прим. ред. I 258
умолчанию это адрес 0); —О = генерировать оверлейную версию объектного кода; —D num = num-размер сегмента данных. learn — команда вызова на выполнение обучающей системы learn [-directory] [subject[lesson[speed]]] lex — команда вызова на выполнение генератора программ лексического анализа текста lex [-fntv] [file] —f = произвести "короткую" трансляцию, используется лишь для генера- ции небольших программ; — п = запретить вывод на стандартный вывод статистики работы команды; — t = вывести результат работы команды на стандартный вывод вместо файла lex.yy.c.; — v= вывести на стандартный вывод статистику работы команды. lint — команда вызова на выполнение верификатора программ, написан- ных на языке программирования Си lint [-abchnpuvx] file ... — а = вывести сводку операторов присваивания переменным типа int зна- чений типа long; — b = вывести сводку операторов break, которые никогда не выполняются; — с =выявить конструкции, мобильность которых вызывает сомнения; — h = произвести проверку правильности программы с помощью набора эвристических тестов; — п = запретить проверку на совместимость с форматом стандартной биб- лиотеки; — р = произвести проверку мобильности данной программы в отношение компилятора с языка программирования Си из состава операционной систе- мы GCOS и операционной системы для ЭВМ семейства IBM; — и = запретить вывод сводки определенных, но не используемых или ис- пользуемых, но не определенных переменных; — V = вывести сводку неиспользованных переменных типа extern ; — х = запретить вывод сводки неиспользованных аргументов функций; In — команда создания синонимов файла; In oldname [newname] login - команда входа в ОС UNIX login [username] look — команда поиска строки в последовательности строк, прошедшей-оор- тировку look [ —df] string [file] — d = содержимое файла file отсортировано в лексикографическом по- рядке; — f = запретить различение строчных и прописных букв; 9* 259
file = имя файла, содержащего последовательность строк, прошедшую сортировку (по умолчанию используется файл /usr/dict/words). lookbib — команда управления библиографической базой данных lookbib [file] ... см. описание команды refer lorder — команда нахождения упорядоченных пар связанных модулей в со- держимом нескольких архивных файлов lorder file ... Is — команда вывода на стандартный вывод содержимого каталога Is [-acdfgilrstu] [name ...] — а= вывести на стандартный вывод список всех входов в каталог; — с = провести сортировку по дате последней модификации описателя файла; — d = вывести на стандартный вывод лишь список имен каталогов; — f = интерпретировать каждый вход в каталог как имя каталога и вывести на стандартный вывод список входов в каталог в том порядке, в котором они были созданы; — g = вывести на стандартный вывод идентификатор группы пользователя вместо идентификатора владельца файла; — i = вывести на стандартный вывод номер описателя файла; — 1 = вывести на стандартный вывод список входов в каталог в "длинном" формате; — г = изменить порядок сортировки на обратный; — s = вывести на стандартный вывод размеры файлов в блоках диска; — t = провести сортировку выводимого списка входов в каталог по време- ни последней модификации; — и = провести сортировку выводимого списка имен файлов по времени последнего доступа к файлу. гп4 — команда вызова на выполнение макропроцессора М4 т4 [file] mail — команда отправления или получения сообщений mail person .... mail [-f file] [ - p] [-r] — ffi le = использовать файл fi le вместо файла mbox; — r = установить порядок приема/передачи сообщений типа "первым при- шел - первым ушел*' (FIFO); — р= не задавать вопросов. make — команда сопровождения больших комплексов программ make [-f mfrle] [-iknrst] file ... — f mfile = использовать файл mfile вместо файла makefile; — i = игнорировать аварийное завершение команд; — к = перейти к следующему входу при аварийном завершении команды; 260
— n = запретить выполнение каких-либо команд, произвести лишь трасси- ровку их выполнения; — г = запретить использование встроенных правил образования окончаний имен файлов; — s = запретить вывод на стандартный вывод содержимого командных строк перед их выполнением; — t = обновить даты последней модификации всех логически связанных файлов без выполнения команд. man — команда вывода на стандартный вывод страниц Руководства man [-ekntw] [chapter] title ... — e = вызвать на выполнение команду eqn; — k = сформатировать выводимый текст для терминала типа Tektrotix 4014; — п = сформатировать выводимый текст с помощью программы nroff и вы- вести его на стандартный вывод; — t = сформатировать текст с помощью команды troff и вывести на фото- наборное устройство; — w = вывести на стандартный вывод только полные имена файлов. mesg — команда запрещения или разрешения приема сообщений mesg [пу] п = запретить прием сообщений, переданный с помощью команды writte; у = разрешить прием сообщений, переданных с помощью команды writte. Если команда вызвана на выполнение без аргументов, то на стандартный вывод будет выведено текущее состояние индикатора "разрешения/запреще- ния приема сообщений". mkdir — команда создания каталога mkdir dirname mv — команда переименования файлов mv oldname newname mv file ... directory neqn — команда форматирования специальных математических выражений для дальнейшего форматирования с помощью программы nroff neqn [option] ... [file] ... См. описание команды eqn. newgrp — команда смены текущего группового имени пользователя newgrp group nice — команда вызова на выполнение команды command с низким уров- нем приоритета nice [-number] command [arg ...] number = десятичное число в диапазоне от 1 до 20, представляющее собой значение уровня приоритета. Самый низкий уровень приоритета 20, по умол- чанию 10. 261
nm — команда вывода на стандартный вывод таблицы символов объект- ных файлов, имена которых пеоечислены в списке аргументов nm [-gnopru] [file ...] — g = вывести на стандартный вывод только имена глобальных переменных; — п = произвести сортировку выводимых имен по значению (адресу) со- ответствующего входа в таблицу символов; — о = вывести на стандартный вывод имя очередного файла в каждой соот- ветствующей ему строке таблицы символов; — р = запретить всякую сортировку выводимых имен; — г = осуществить сортировку выводимого списка имен в обратном по- рядке; — и = вывести на стандартный вывод только имена символов типа undefined, nohup — вызвать на выполнение команду command таким образом, чтобы процесс, в рамках которого она будет выполняться, игнорировал сигнал SIGHUP nohup command [arg ...] nroff — команда форматирования текста nroff [option] ... file ... options — e = произвести равномерную раздвижку слов в сформатированной стро- ке текста; — h = заменить последовательность символов на символ tab везде, где это возможно; — i — после завершения форматирования текста, содержащегося в файлах, перейти к осуществлению операции ввода со стандартного ввода; — mname = использовать при форматировании макродирективы формати- рования, содержащиеся в файле /usr/lib/tmac/tmac.name; — пп — п — номер первой сформатированной страницы; — olist = вывести на стандартный вывод лишь те из сформатированных страниц текста, номера которых перечислены в списке list; — q = задать режим одновременного осуществления операций ввода-вы- вода; — ran = присвоить внутреннему регистру а значение п; — sn = приостанавливать осуществление операции вывода после вывода каждых п страниц сформатированного текста (по умолчанию после каждой страницы); -Tname = форматировать текст с учетом того, что результат форматирова- ния будет выведен на терминал типа name, где name — это строка символов вида 37 или tn300 или 300S и т.д. od — вывести на стандартный вывод содержимое произвольного файла od [-bedox] [file] [ + ] [offset[.][b]] — b = осуществить побайтный вывод содержимого файла в восьмеричном виде; — с = осуществить побайтный вывод содержимого файла в коде ASCII; 262
— d = осуществить пословный вывод содержимого файла в целом десятич- ном виде; — о = осуществить пословный вывод содержимого файла в восьмеричном виде; —х = осуществить пословный вывод содержимого файла в шестнадцати- ричном виде; offset = начать вывод содержимого файла с байта (блока, если исполь- зован флаг Ь) с порядковым номером offset; . = воспринимать число как десятичное. passwd —команда смены пароля пользователя passwd [name] plot — команда, представляющая собой набор фильтров для осуществле- ния вывода-графической информации на одно из стандартных внешних уст- ройств отображения графической информации. plot [-Tterm [raster]] term = 4014, 450, 300, 300S, ver pr — команда форматирования и вывода на стандартный вывод содержи- мого файлов pr [option] ... file ... options — n = сформатировать текст в п колонок; +п = начать форматирование и вывод со страницы номер п; — h string = использовать содержимое строки string в качестве заголовка каждой из сформатированных страниц текста; — In = использовать п в качестве длины страницы (по умолчанию длина страницы 66 строк); — ш = сформатировать и вывести на стандартный вывод содержимое не- скольких файлов так, чтобы содержимое каждого файла занимало отдельную колонку; — sc = использовать в качестве разделителя колонок сформатированного текста символ с; — t = запретить вывод заголовка страницы и стандартного текста в нижней части страницы; — wn = установить ширину страницы равной п (по умолчанию 72 символа). prof — команда получения и вывода на стандартный вывод профиля вре- мени выполнения некоторой программы prof [-a] [-I] [-v] [-low [-high] [file] — а = вывести на стандартный вывод список всех переменных; — 1 = осуществить сортировку списка переменных по значению соответст- вующего входа в таблицу символов; — V = вывести на стандартный вывод профиль времени выполнения неко- торой программы в графическом виде; low, high = интересующий интервал (в процентах) по оси ординат (по 263
умолчанию используется 0,100). ps — команда вывода на стандартный вывод информации о состоянии про- цессов ps [aklx] [namelist] а = вывести на стандартный вывод информацию о состоянии зсех процес- сов и имена ассоциированных с ними терминалов; к = использовать файл /usr/sys/core вместо файла /dev/mem; 1 = вывести на стандартный вывод информацию о состоянии процессов в "длинном" формате; х = вывести информацию о состоянии всех процессов. namelist = имя файла, содержащего ядро ОС UNIX (по умолчанию /unix). ptx — команда вывода на стандартный вывод упорядоченного предметного указателя ptx [option] ... [input [output] ] options: — b break = использовать символы, содержащиеся в файле break в качест- ве разделителей слов; - f = запретить различение строчных и прописных букв при сортировке содержимого файла input; — g n = отделить одну колонку от другой п символами (по умолчанию вы- вод производится тремя символами); — i ignore = запретить использование в качестве ключевых слов слова, со- держащиеся в файле ignore; — о only = использовать в качестве ключевых слов лишь слова, содержа- щиеся в файле only; — г= использовать первое слово каждой строки для ссылки; — 1 = подготовить текст предметного указателя для вывода на фотонабор- ное устройство; — wn = установить ширину строки равной п (по умолчанию ширина строки 72 символа). pwd — команда вывода на стандартный вывод абсолютного полного имени текущего каталога pwd ranlib — команда преобразования архива в библиотеку произвольного дос- тупа ranlib archive ... ratfor — команда вызова на выполнение препроцессора с языка програм- мирования RATFOR ratfor [ — С] [~h] [-6х] [file] ... — С = копировать комментарии на стандартный вывод; — h = преобразовать строку, помещенную между двумя последовательны- ми символами в холеритовскую форму; 264
— 6х = использовать символ х в качестве указателя продолжения текста в следующей строке (символ должен быть помещен в 6-й позиции). refer — команда нахождения, форматирования и вывода на стандартный вывод ссылок на библиографию, содержащихся в некотором тексте refer [option] ... options — ar = поменять местами первые г имен автора и все остальные его имена; — а = вывести имена автора в обратном порядке; — b = игнорировать все флаги, указанные в тексте; — cs = преобразовать первые буквы слов в строчные, если они содержатся в строке s; — е = накапливать собранные ссылки; — кх = использовать символ х для спецификации ссылок; — Im, п = использовать в качестве ссылки последовательность из первых m букв последнего имени автора и п последних цифр года издания; — р f = использовать содержимое файла f в качестве списка подлежащих отысканию ссылок; —skeys = осуществить сортировку обнаруженных ссылок по полям, клю- чевые буквы которых содержатся в строке keys. rev — команда перестановки строк файла в обратном порядке rev [file] ... rrn — команда удаления файлов rm [-fir] file ... — f = не требовать подтверждения необходимости удаления файла в случае отсутствия разрешения на доступ к указанному файлу по записи; — i = организовать процесс удаления файлов в виде диалога; — г= удалять входы в каталог рекурсивно. rmdir — команда удаления каталогов rmdir dir roff — команда форматирования текста roff [ + п] [ — n] [-h] [-s] file ... + n = начать форматирование текста со страницы номер п; — п = прекратить форматирование текста после форматирования страницы с номером п; — h = заменить последовательность символов о на символ tab везде, где это возможно; — s = приостанавливать вывод сформатированного текста после вывода каждой страницы, sed — команда вызова на выполнение неинтерактивного редактора текстов sed sed [-е script] [-f cmdfile] [~n] [file] ... — e = использовать следующий аргумент в качестве команды редактора текстов sed; 265
— f = использовать следующий аргумент в качестве имени файла, содержа- щего последовательность команд редактора текстов sed; — п = вывести на стандартный вывод только строки, обработанные коман- дой р редактора текстов sed. sh — команда вызова на выполнение интерпретатора команд shell sh [option] ... [arg] optiots — c string = интерпретировать содержимое строки символов string в ка- честве команды ОС UNIX; — е = завершить выполнение команды при возвращении ею нулевого кода завершения, если интерпретатор команд shell вызван на выполнение в неин- терактивном режиме; — i = вызвать на выполнение интерпретатор команд shell в интерактивном режиме; — к = интерпретировать все аргументы команд вида name=vaiue как опера- ции определения переменных интерпретатора команд shell; — п = ввести со стандартного ввода команды, но не выполнять их; — г = отменить режим, специфицированный опцией —к; — s = ввести команды со стандартного ввода; — t = завершить выполнение интерпретатора команд shell после выполне- ния им одной команды ОС UNIX; — и = считать неустановку значений переменных интепретатора команд shell ошибкой, возникшей при осуществлении операции подстановки; — v = выводить вводимые строки на стандартный вывод по мере их ввода; — х = выводить на стандартный вывод команды вместе с их аргументами по мере их выполнения. size — команда определения и вывода на стандартный вывод размеров объектного файла в байтах size [ofile] ... sleep - команда задержки выполнения некоторой команды ОС UNIX на заданный интервал времени sleep seconds seconds = десятичное целое число, специфицирующее время задержки вы- полнения команды в секундах, находится в диапазоне от 1 до 65 535. sort — команда сортировки содержимого нескольких файлов как одного целого sort [-cmubdfinrtx] [ + m.n [-т.п]] ... [-о пате] [-Тdir] [пате] ... — с = произвести проверку порядка следования строк отсортированных файлов; — m = объединить содержимое входных файлов в одно целое без сорти- ровки; — и = вывести на стандартный вывод лишь одну из совпадающих строк; 266
— b = игнорировать символы i_i и tab, если с них начинается строка; — d = установить алфавитный порядок сортировки; - f = запретить различение прописных и строчных букв; — i = игнорировать непечатные символы, т. е. символы, коды которых вы- ходят за пределы диапазона 040 — 0176; — п = установить порядок сортировки строк, содержащих лишь числовые величины по их значениям; — г = установить обратный порядок сортировки; — tx= установить символ х в качестве разделителя полей; +m.n = считать первым символом контекста ключа сортировки (п + 1) -й символ (т + 1)-го поля; — т.п = считать последним символом контекста ключа сортировки (п +1) -й символ (т + 1) -го поля; — о пате = произвести вывод результатов сортировки в файл name; — Т dir = использовать для создания временных файлов каталог dir. spell — команда нахождения ошибок в правописании spell [- b] [ —v] [ -х] [file] ... — b = использовать правила правописания, принятые в Великобритании; — V = вывести на стандартный вывод слова, отсутствующие в списке слов- эталонов; — х = вывести на стандартный вывод лишь корни слов, содержащих ошиб- ки правописания. spline — команда интерполяции гладкой кривой spline [-а] [-к] [ — п] [ — р] [ — х] — а = установить автоматическое задание абсциссы, следующий аргумент должен задавать величину шага абсциссы; — к = использовать следующий аргумент для вычисления граничных по оси ординат значений кривой; — п = установить число выводимых точек кривой равным значению следу- ющего аргумента; — р = установить равенство первых производных на концах отрезка опре- деления; — х = установить нижнюю и верхнюю границы значений рассматриваемой кривой с помощью одного или двух следующих аргументов. split — команда расчленения содержимого файла и вывода его частей в не- сколько выходных файлов split [ — n] [file [name]] — n = расчленить содержимое входного файла на части, содержащие по п строк текста каждая (по умолчанию используется число 1000); name = установить контекст name в качестве корня имени выходного фай- ла (по умолчанию используется корень вида file). strip - команда удаления из файла, содержащего объектный код или 267
выполняемую программу таблицы символов и битов перемещения strip file ... struct — команда структурирования исходного текста программ, написан- ных на языке программирования Фортран struct [option]... [file] ... options — a = преобразовать последовательности операторов else if в операторы switch; — Ь= генерировать операторы goto вместо многоуровневых последова- тельностей операторов break; — еп = задать значениям п режим работы оптимизатора операторов цикла; — i = запретить преобразование вычисляемых операторов goto в операторы switch; — п = генерировать операторы go to вместо многоуровневых последова- тельностей операторов next; —s = считать формат содержимого входного файла стандартным. stty — команда установки технических характеристик терминала stty [-]option ... Если аргументу команды предшествует символ —, то это означает отрицание значения указанного аргумента. even = разрешить контроль по четности; odd = разрешить контроль по нечетности; raw ~ режим небуферизованного ввода; cbreak = буквальная передача всех символов; nl = разделителем строк может быть только символ newline; echo = установить режим эхо; lease = установить режим преобразования прописных букв в строчные буквы; tabs = терминал имеет технические средства для аппаратной реализации горизонтальной табуляции; ек = в качестве символов для обозначения удаления последнего введенного символа и удалейия последней введенной строки приняты символы # и erase с = ввод символа с приводит к удалению последнего введенного сим- вола ; kill с = ввод символа с приводит к удалению последней введенной строки; hup = установить таймаут для терминала после выполнения следующей команды; О = немедленно установить таймаут для терминала; сгп = установить задержку после ввода символа return; nln = установить задержку после ввода символа linefeed;. tabn = установить задержку после ввода символа tab; ff п = установить задержку после ввода символа formfeed; tty33 = используется терминал типа Teletype model 33; tty37 = используется терминал типа Teletype model 37; 268
vtO5 — используется терминал типа DEC VT05; tn3OO= используется терминал типа GE TermiNet 300 ; ti700= используется терминал типа Texas Instruments 700; tek= используется терминал типа Tektronix 4014; Набор возможных скоростей обмена (бит/с): 50 75 110 134 150 200 300 600 1200 2400 4800 9600. $ц —команда перехода в режим привилегированного пользователя su [name] name = входное имя пользователя. sum — команда получения контрольной суммы файла sum file tabs — команда установки режима горизонтальной табуляции tabs [- n] [terminal] —п = запретить отступ от левого края; terminal = имя терминала, для которого производится данная установка. tail — команда вывода на стандартный вывод части содержимого'файла tail [[+ -]number[lbc]] [file] +numberl = вывести на стандартный вывод строки файла, начиная со стро- ки с номером number; —numberl = вывести на стандартный вывод последние number строк файла; +numberb = вывести на стандартный вывод содержимое блоков файла, на- чиная с блока номер number; —numberb = вывести на стандартный вывод содержимое последних number блоков файла; (-number с= вывести на стандартный вывод содержимое файла, начиная с символа номер number; —number с= вывести на стандартный вывод последние number символов файла. tar ~~ команда обслуживания архива на магнитной ленте tar [key] [name] ... key = строка символов, содержащая, по крайней мере, одну специфика- цию выполняемой функции и, возможно, несколько спецификаций режима выполнения этой функции; функции: с = создать на магнитной ленте новый архив (предполагает использование спецификации г); г = ввести в архив новый файл, расположив его физически после послед- него введенного в архив файла; t = вывести на стандартный вывод список имен, введенных в архив файлов; и = ввести в архив файл name, если архив не содержит файла с таким име- нем или если с момента введения в архив файл был уже модифицирован; х = извлечь файл name из архива, хранящегося на магнитной ленте, уста- 269
новленной на накопителе на магнитной ленте с номером из следующего списка: О,... ,7 — спецификатор номера накопителя; Ь = использовать следующий аргумент для спецификации числа блоков диска, составляющих одну запись на магнитной ленте; f = использовать следующий аргумент для спецификации внешнего уст- ройства, обрабатываемого специфицированной функцией; 1 = вывести на стандартный вывод сообщение о наличии неудовлетворен- ных ссылок на файлы, обрабатываемые специфицированной функцией; w = вывести на стандартный вывод запрос на подтверждение необходи- мости выполнения специфицированной функции; v = вывести на стандартный вывод протокол выполнения специфициро- ванных функций. tbl — команда форматирования таблиц; tbl [file] ... tc — команда эмуляции интерфейса фотонаборного устройства tc [-t] [-sn] [-plfpicp]] [file] — t = осуществить непрерывный (без ожидания подтверждения на вывод очередной страницы) вывод сформатированного текста; — sn = пропустить первые п страниц текста; — pl = установить длину страницы текста равной I, где 1 измерено в: р = пунктах; i = дюймах; с = сантиметрах; р = цицеро4 (используется по умолчанию). tee — команда раздвоения канала tee [-a] [-i] [file] ... —а = добавить информацию в файл, неудаляя его прежнего содержимого; -i = игнорировать прерывания. test — команда проверки выполнения некоторого условия; test ехрг ехрг = булевское выражение, составленное из некоторых из нижеперечис- ленных компонент Компонента булевского Принимает значение "истина", если выражения —rfiie имеется разрешение на доступ к файлу file по чтению; 1 Цицеро — единица длины в типометрической системе (12 п. = 4,5 мм), а также ти- пографский шрифт, кегль (размер) которого равен 12 п. Назван так потому, что шриф- том такого размера впервые (в 1467 г.) были напечатаны "Письма Цицерона". (Прим, ред.) 270
—w file имеется разрешение на доступ к файлу file по записи; -f file -d file —s file файл fi le не является каталогом; файл fi le является каталогом; файл file содержит хотя бы один байт информа- ции; —t [frides] значение аргумента f i Ides является номером опи- сателя специального файла, соответствующего данному терминалу; -z SI -u SI SI =S2 длина строки S1 равна нулю; длина строки S1 не равна нулю; содержимое строки S1 совпадает с содержимым SI 1=S2 строки S2; содержимое строки S1 не совпадает с содержи- nl op n2 мым строки S2; выполнено соответствующее соотношение между целыми числами nl и п2, где соотношение задает- ся любой из нижеперечисленных операций срав- нения: op = —eq, —пе, —gt, —ge, —It, —le Указанные компоненты могут быть сгруппированы в булевское выражение с помощью следующих логических операций: ! = логическое отрицание; ~а= логическое И; — о= логическое ИЛИ; ( ) для определения последовательности выполнения логических операций, time — команда определения времени выполнения заданной команды ОС UNIX time command [arg] ... tk — команда вывода содержимого текстового файла на терминал типа "Tektronix 4014"; tk [-t] [-n] [-р] [file] — t = осуществить непрерывный (без ожидания подтверждения на вывод очередной страницы) вывод текста; — р = установить длину страницы равной п строк; — п = вывести содержимое файла f i 1е в л колонок. touch — команда обновления даты последней модификации файла touch [-с] file ... —с = запретить создание файла в том случае, если файл fi le не существует, tp — команда обслуживания архива на магнитной ленте или внешнем запо- минающем устройстве типа DECtape tp [key] [name] ... key = строка символов, содержащая, по крайней мере, одну специфика- 271
цию выполняемой функции и, возможно, несколько спецификаций режима выполнения этой функции; функции: d = удалить из архива файл name; г = ввести в архив файл name, а в случае существования в составе архива такого файла — произвести замещение; t = вывести на стандартный вывод список имен файлов, внесенных в архив файлов; и = ввести в архив файл name, если архив не содержит файла с таким име- нем или если с момента внесения в архив файл уже был модифицирован; х = извлечь файл name из архива, хранящегося на внешнем запоминающем устройстве с номером из следующего списка: О, . . . , 7 — спецификатор номера накопителя; с= создать новый архив; f= использовать следующий аргумент для спецификации внешнего уст- ройства, обрабатываемого специфицированной функцией; i = игнорировать ошибки ввода-вывода при осуществлении операций об- мена; ш = в качестве внешнего запоминающего устройства использовать нако- питель на магнитной ленте (по умолчанию использовать внешнее запоминаю- щее устройство типа DECtape); v = вывести на стандартный вывод протокол выполнения специфициро- ванных функций; w = выполнить специфицированную функцию лишь по получении под- тверждения. tr — команда копирования символов со стандартного ввода на стандартный вывод tr[-cds] [stringl] [string2] — с = дополнить содержимое строки stringl до полного набора символов (коды ASCII которых находятся в диапазоне 01—0377); — d = игнорировать содержимое строки stringl; — s= игнорировать повторяющиеся символы в строке string2. troff — команда форматирования текста для вывода на фотонаборное уст- ройство. troff [option] ... file ... options — a = вывести на стандартный вывод сформатированный текст; — b = вывести на стандартный вывод информацию о том, свободно ли фо- тонаборное устройство; — f = запретить прогон бумаги и останов фотонаборного устройства по окончании вывода сформатированного текста; — g = сформатировать текст для вывода его на фотонаборное устройство, работающее под управлением операционной системы GCOS; — i = перейти к вводу со стандартного ввода после окончания ввода текста 272
из файлов file...; — m name = использовать при форматировании макродирективы формати- рования, находящиеся в файле /usr/lib/tmac/tmac.name; — nn = п = номер первой ^форматированной страницы; — olist = вывести на фотонаборное устройство лишь те из сформатирован- ных страниц текста, номера которых перечислены в списке list; — рп = задать размер выводимых символов равным п пунктам; - q = задать режим одновременного выполнения операций ввода-вывода; — ran = присвоить внутреннему регистру а значение п; — sn = приостановить осуществление операций вывода после вывода каж- дых п страниц сформатированного текста (по умолчанию после каждой стра- ницы) ; — t = вывести результат форматирования на стандартный вывод; — w= ожидать готовности фотонаборного устройства. tsort — команда сортировки содержимого файла методом наложения tsort file tty — команда вывода на стандартный вывод имени терминала tty uniq — команда нахождения в содержимом файла повторяющихся строк uniq [-cdu] [ + n] [-n] [input [output]],' — с = вывести на стандартный вывод значение счетчика повторений строк для каждой из повторяющихся строк файла input; — d = вывести в файл output по одной копии каждой из повторяющихся строк файла input; — и = вывести в файл output все неповторяющиеся строки файла input; + п = пропустить при сравнении п первых полей каждой строки; — п = пропустить при сравнении п первых символов в каждой строке. units — команда перевода величин из одной системы измерений в другую units wait — команда ожидания завершения процессов wait wc — команда вывода на стандартный вывод количества символов, слов и строк, содержащихся в файле wc [-clw] [file] ... — с = вывести на стандартный вывод число символов, содержащихся в файле file; — 1 = вывести на стандартный вывод число строк, содержащихся в файле file; — w= вывести на стандартный вывод число слов, содержащихся в файле file; who — команда вывода на стандартный вывод имен пользователей, работа- 273
ющих в данный момент под управлением ОС UNIX who [who —file] [am i] без аргументов = вывести на стандартный вывод имена пользователей, рабо- тающих в данный момент под управлением ОС UNIX; who—fi le = использовать файл who—f I le вместо файла /etc/utmp; am i = вывести на стандартный вывод входное имя пользователя, вызвав- шего на выполнение эту команду. write — команда передачи строк текста, вводимых с одного терминала на другой. write user [ttyname] user = входное имя пользователя—получателя; уасс — команда вызова на выполнение компилятора компиляторов уасс уасс [-dv] [grammar] — d = создать файл y.tab.h и поместить в него все конструкции типа de- fine; — V = вывести в файл y.output грамматические таблицы и список неопре- деленностей, содержащихся в заданной грамматике. ПРИЛОЖЕНИЕ Б. РЕДАКТОР ТЕКСТОВ ed Это приложение рассчитано на использование в качестве справочника по редактору текстов ed. Если Вам требуются более подробные объяснения и примеры, то для большинства описываемых ниже возможностей редактора текстов ed Вы можете найти их в гл. 3. ВЫЗОВ РЕДАКТОРА ТЕКСТОВ ed ed — filename Использование необязательного аргумента приводит к тому, что редактор текстов ed не будет осуществлять вывод на терминал числа символов, введен- ных из файла, и числа символов, выведенных в файл. Этот аргумент особен- но полезен при создании командных файлов, в которых используется редак- тор текстов ed. Если при вызове редактора текстов ed было указано имя файла, то редактор текстов ed использует этот файл для ввода информации и вывода ее в результате использования команды w без аргументов. КОМАНДЫ РЕДАКТОРА ТЕКСТОВ а— команда добавления строк line—по а В результате выполнения этой команды строки, вводимые за строкой, со- держащей эту команду, добавляются после строки с адресом line—по. Если не указана никакая строка, то строки будут добавлены после текущей строки. Для того чтобы завершить добавление строк, необходимо ввести строку, со- стоящую из единственного символа .. В результате выполнения команды до- 274
бавления строк а текущей строкой становится последняя добавленная строка. с — команда замены строк first, last с В результате выполнения этой команды строки текста, начиная со строки с адрасом first и кончая строкой с адресом last, заменяются на строки, вво- димые вслед за этой командой. Заменяющие строки завершаются строкой, состоящей из единственного символа .. Для замены одной строки достаточно указать только ее адрес. В случае,если не указана никакая строка, замене подвергается текущая строка. В результате выполнения команды замены строк с текущей строкой становится последняя заменяющая строка. d — команда удаления строк first, last d В результате выполнения этой команды строки текста, начиная со строки с адресом first и кончая строкой с адресом last, удаляются из текста. Для уда- ления одной строки достаточно указать только ее адрес. Если не указана ни- какая строка, то по умолчанию удаляется текущая строка. В результате вы- полнения команды удаления строк d текущей строкой становится строка, следующая за последней удаленной строкой. е— команда перехода к редактированию другого файла е filename В результате выполнения этой команды осуществляется переход к редак- тированию файла с именем filename. Результаты предыдущей работы будут утеряны (о чем на терминал будет выведено соответствующее предупрежде- ние) , если перед выполнением команды перехода к редактированию другого файла е не будет выполнена команда записи в выходной файл w, а текст был модифицирован. Имя файла filename будет запомнено редактором текстов ed для последующего использования. Е Эта команда полностью аналогична предыдущей, за исключением того, что в случае, если перед ее выполнением не была выполнена команда записи в выходной файл w, а текст был модифицирован, на терминал не выводится никакого предупреждения. Эта команда не имеет широкого распространения. f - команда запоминания имени файла f filename В результате выполнения этой команды имя файла filename запоминается редактором текстов ed для последующего использования. В результате вы- полнения этой команды запомненное имя файла будет выведено на терминал. Это имя файла впоследствии используется при выполнении команды записи в выходной файл w, примененной без аргумента. g— команда указания глобальности Эта команда используется в двух различных контекстах. Во-первых, она используется для распространения действия некоторой другой команды на все строки текста, содержащие некоторый шаблон first, last g/ expression/commands 275
В результате выполнения подобной команды будут найдены все строки текс- та, заключенные между строкой с адресом first и строкой с адресом last, со- держащие указанный шаблон, а затем к найденным строкам будут примене- ны указанные команды commands, которые могут размещаться более чем в одной строке, если все строки кроме последней, завершаются символом \. Если диапазон адресов строк текста не указан, то поиск строк, содержащих указанный шаблон, осуществляется по всему тексту. Во-вторых, эта команда используется для распространения действия команды контекстной замены s на все вхождения указанного контекста в некоторую строку. i - команда вставки строк line—no i Эта команда полностью аналогична команде добавления строк а, за исклю- чением того, что вставленные строки добавляются не после строки с адресом line-no, а перед ней. j — команда объединения строк first,last j В результате выполнения этой команды строки текста, начиная со строки с адресом first и кончая строкой с адресом last, объединяются в одну строку. Если указана только одна строка, то она объединяется со следующей за ней строкой или вообще ничего не происходит. Это зависит от используемой Вами версии редактора текстов ed. Мы рекомендуем Вам выяснить это опыт- ным путем. Если не специфицирована никакая строка, то во всех известных нам версиях редактора текстов ed текущая строка объединяется со следую- щей за ней строкой. к — команда присвоения метки строке line—no k lower—case—letter В результате выполнения этой команды строка с адресом line—по будет помечена. Если не указана никакая строка, то будет помечена текущая стро- ка. После выполнения этой команды помеченная строка может быть указана в любой команде не только с помощью своего номера, но также и с помощью присвоенной этой строке метки. В качестве меток строк используются строч- ные буквы латинского алфавита. Для указания строки с помощью присвоен- ной этой строке метки необходимо использовать метку строки, предварив ее символом > (например, строка, помеченная с помощью команды ка, может быть впоследствии специфицирована с помощью аргумента 'а). Возможность помечать строки текста позволяет избежать проблем, связанных с измене- нием номеров строк в процессе редактирования. С помощью одной метки можно пометить только одну строку. 1— команда вывода на терминал всех символов first,last 1 В результате выполнения этой команды на терминал выводятся строки текста, начиная со строки с адресом first и кончая строкой с адресом last. В случае если указан адрес только одной строки, то на терминал будет выведена только эта строка. Если не указана никакая строка, то на терминал будет вы- 276
в еден а текущая строка. В результате выполнения команды вывода на терми- нал всех символов 1 текущей строкой становится последняя выведенная на терминал строка. Эта команда позволяет увидеть положение служебного символа в строке, изображая его с помощью некоторой стандартной комби- нации печатных символов, при этом символ tab изображается с помощью символа >, символ backspace изображается с помощью символа <, а прочие служебные символы изображаются с помощью своего восьмеричного кода, перед которым помещен символ\ (например, символ formfeed изображается с помощью комбинации символов \014). Основным назначением этой коман- ды является выявление "мусора" в строках. (См. также команду вывода на терминал р.) m — команда перемещения строк first,last m after В результате выполнения этой команды строки текста, начиная со строки с адресом first и кончая строкой с адресом last, перемещаются таким образом, что оказываются размещенными после строки с адресом after. Если указан адрес только одной строки, то будет перемещена только эта строка. Если не указана никакая строка, то будет перемещена только текущая строка. В ре- зультате выполнения команды перемещения строк m текущей строкой ста- новится последняя перемещенная строка. р— команда вывода на терминал first, last р В результате выполнения этой команды на терминал выводятся строки текста, начиная со строки с адресом first и кончая строкой с адресом last. При этом не делается никаких попыток сделать видимыми служебные символы. Если указан адрес только одной строки, то на терминал будет выведена толь- ко эта строка. Если не указана никакая строка, то на терминал будет выведе- на текущая строка. В результате выполнения команды вывода на терминал р текущей строкой текста становится последняя выведенная на терминал стро- ка. Команды ри 1 могут быть добавлены к большинству команд с целью сра- зу же увидеть результаты их выполнения (чаще всего это используется для команды контекстной замены s). q — команда завершения редактирования Результатом выполнения этой команды является завершение выполнения редактора текстов ed без записи результатов редактирования в выходной файл. Если перед выполнением этой команды не была выполнена команда записи в выходной файл w, а текст был модифицирован, на терминал будет выведено соответствующее предупреждение. Q Эта команда полностью аналогична предыдущей, за исключением того, что, если перед ее выполнением не была выполнена команда записи в выходной файл w, а текст был модифицирован, на терминал не выводится никакого предупреждения. г — команда ввода из входного файла line—no г filename 277
В результате выполнения этой команды будет осуществлено чтение текста из входного файла filename и добавление этого текста к уже редактируемому тексту после строки с адресом line—по. Если не указана никакая строка, то прочитанный из входного файла текст будет добавлен после последней строки уже редактируемого текста. Если не указано имя файла, то будет использова- но имя файла, ранее запомненное редактором текстов ed. Если до выполнения этой команды редактором текстов ed не было запомнено никакого имени файла, то имя файла обязательно должно быть указано в этой команде. Тогда указанное в этой команде имя файла будет запомнено редактором текстов ed для дальнейшего использования. В качестве аргумента line—по в этой коман- де может быть указан 0. В этом случае прочитанный из входного файла текст будет добавлен перед редактируемым текстом. s — команда контекстной замены first,last s /ab/cd/gp В результате выполнения этой команды в строках текста, начиная со стро- ки с адресом first и кончая строкой с адресом last, каждое первое вхождение в строку контекста ab будет заменено на контекст cd. В случае, если указан адрес только одной строки, то контекстная замена будет выполнена только в этой строке. Если не указана никакая строка, то контекстная замена будет выполнена в текущей строке. Использование команды указания глобальности g совместно с этой командой приводит к замене всех вхождений контекста ab на контекст cd в каждой из специфицированных строк. Эта команда мо- жет быть использована также совместно с командами р и 1 для того, чтобы немедленно увидеть результаты проведенной контекстной замены. Если контекст ab является пустым контекстом, т.е. не содержит ни одного сим- вола, то в качестве заменяемого контекста будет использован шаблон, указанный в предыдущей команде контекстной замены или в последней вы- полненной команде контекстного поиска. Использование символа & в ка- честве первого символа контекста cd приводит к преобразованию заменяю- щего контекста cd в контекст abed. Символ /, используемый в качестве ограничителя контекстов, может быть заменен на любой другой не входящий ни в один из указанных контекстов символ, за исключением символов i_j и tab. В результате выполнения команды контекстной замены s текущей стро- кой становится последняя строка текста, в которой была осуществлена кон- текстная замена. t — команда копирования строк first, last t after Эта команда выполняется аналогично команде перемещения строк т, за исключением того, что вместо перемещения строк она осуществляет копиро- вание строк. Эта команда часто бывает полезна в тех случаях, когда в тексте часто встречаются повторяющиеся фрагменты. undo— команда отмены последней выполненной контекстной замены s Выполнение этой команды приводит редактируемый текст к виду, который он имел до выполнения последней команды контекстной замены s при усло- 278
вии, что эта команда была применена к текущей строке. Эта команда очень удобна для идиотов. v- ?? См. описание команды указания глобальности g, которой она эквивалент- на во всем, за исключением того, что указанные команды будут применены к строкам, не содержащим указанного шаблона. w — команда вывода в выходной файл first,last w filename В результате выполнения этой команды строки текста, начиная со строки с адресом first и кончая строкой с адресом last, будут записаны в выходной файл filename. Если не указана никакая строка, то в выходной файл будут за- писаны все строки текста. Если не указано имя файла, то в качестве выход- ного файла будет использован файл, имя которого было ранее запомнено ре- дактором текстов ed либо в результате его вызова, либо в результате выпол- нения команды перехода к редактированию другого файла е, либо в резуль- тате выполнения команды чтения из входного файла г, либо в результате вы- полнения команды запоминания имени файла f. Если до выполнения этой команды редактором текстов ed не было запомнено никакого имени файла, то указанное в этой команде имя файла будет запомнено редактором текстов для дальнейшего использования. W Эта команда полностью аналогична предыдущей, за исключением того, что выводимый в выходной файл текст не заменяет старого содержимого выходного файла, а будет добавлен к старому содержимому выходного фай- ла после него. х — команда шифрования Эта команда запрашивает у Вас ключ, который будет использован в ка- честве ключа для шифровки (дешифровки) читаемого/записываемого текста. Алгоритм шифровки, используемый этой командой, описан в crypt (1). ! — непроизносимо ! any unix command Эта команда позволяет Вам, не выходя из редактора текстов ed, выполнять команды ОС UNIX, при этом выполнение редактора текстов ed приостанав- ливается до тех пор, пока не завершится выполнение указанной команды ОС UNIX. КОНТЕКСТНЫЙ поиск КОНТЕКСТНЫЙ ПОИСК В ПРЯМОМ НАПРАВЛЕНИИ /expression/ В результате выполнения этой команды по всему тексту осуществляется контекстный поиск контекста, указанного с помощью аргумента expression в прямом направлении,т. е. начиная от строки, следующей за текущей строкой. 279
в сторону конца текста, а затем, если указанный контекст не найден, от нача- ла текста до текущей строки. Эту команду можно использовать для задания адреса строки. КОНТЕКСТНЫЙ ПОИСК В ОБРАТНОМ НАПРАВЛЕНИИ ?expression? Эта команда аналогична предыдущей, за исключением того, что контекст- ный поиск контекста, указанного с помощью аргумента expression, ведется в обратном направлении, т.е. начинается со строки, предшествующей текущей строке, и ведется в сторону начала текста, а затем, если указанный контекст не найден, продолжается от конца текста до текущей строки. Независимо от направления контекстного поиска, если в команде контек- стного поиска указан пустой контекст, то осуществляется контекстный по- иск того контекста, который был указан в последней выполненной команде контекстного поиска или в последней выполненной команде контекстной за- мены s. СПЕЦИФИКАЦИЯ СТРОК Строки могут быть специфицированы своими номерами или с помощью вышеупомянутых команд контекстного поиска. Кроме того, имеется два символических адреса строк: символ ., обозначающий текущую строку, и символ $, обозначающий последнюю строку редактируемого текста. При спецификации строк можно использовать выражения, включающие операции + и —, что позволяет применять относительную адресацию, например $—5 специфицирует строку, отстоящую на пять строк от конца редактируемого текста. КОНТЕКСТЫ Контексты используются в командах контекстного поиска и командах контекстной замены s. В состав контекстов могут входить любые символы, в том числе и символы, имеющие для редактора текстов ed специальный смысл. Если обычные (т.е. не имеющие для редактора текстов ed специально- го смысла) символы, входящие в состав контекстов, обозначают сами себя, то символы, имеющие для редактора текстов ed специальный смысл и вхо- дящие в состав контекстов, соответствуют некоторым группам символов. Ниже приведен список символов, имеющих для редактора текстов ed специ- альный смысл: А — соответствует символу, стоящему перед первым символом строки; $ — соответствует символу, стоящему после последнего символа строки; . — соответсвует произвольному символу; * — соответствует произвольному числу символов, ему предшествующих, включая ни одного; [ — за этим символом всегда следует некоторая последовательность сим- 280
волов, завершаемая символом ], соответствует произвольному символу из этой последовательности. При этом внутри этой последовательности симво- лов можно использовать символ —. Так, [1—5] является сокращенной фор- мой записи [12345]. Если первым символом в последовательности симво- лов, заключенной между символами [и], является символ Л, то смысл всей конструкции меняется на противоположный. Так, [Л1—5] соответствует произвольному символу, кроме символов 1, 2, 3, 4, 5. Произвольная часть произвольного контекста может быть заключена меж- ду символами \( и символами \). Тогда использование в качестве заменяю- щего контекста последовательности символов \1 соответствует первому кон- тексту, заключенному между символами \ ( и символами \), использование в качестве заменяющего контекста последовательности символов \2 соответст- вует второму контексту, заключенному между символами \( и \) и т.д.. При- веденный ниже пример иллюстрирует эту возможность: s/\(q\)b\(c\)/\2q\1 / Выполнение команды, приведенной в этом примере, приведет к замене кон- текста abc на контекст cqa. Эта возможность, предоставляемая редактором текстов ed, обычно с трудом понимается и используется крайне редко. Редактор текстов ed игнорирует символ NULL и все символы, которые следуют за самым последним символом newline в тексте. Длина строк, кото- рые можно редактировать с помощью редактора текстов ed, ограничена 512 байтами. Длина файлов, которые можно редактировать с помощью редак- тора текстов ed, не должна превышать 128К символов. Выведенное на терминал сообщение об ошибке вида ?ТМР означает, что возникли какие-то проблемы с временным файлом, используемым для хра- нения редактируемого текста. Как правило, эти проблемы связаны с недос- татком места. ПРИЛОЖЕНИЕ В. СИНТАКСИС ЯЗЫКА ПРОГРАММИРОВАНИЯ shell КОМАНДЫ Простые команды состоят из одного или нескольких слов, разделенных с помощью символа и завершаются символом newline или ; . Первое слово представляет собой имя вызываемой на выполнение команды, а все остальные слова передаются вызываемой на выполнение команде в качестве ее аргументов. В.1. ПЕРЕНАЗНАЧЕНИЕ СТАНДАРТНОГО ВВОДА-ВЫВОДА Обычно стандартный вывод назначен на терминал. Стандартный вывод может быть переназначен на файл с произвольным именем следующим образом: команда > имя—файла 281
Стандартный ввод, обычно назначенный на терминал, может быть пере- назначен на файл с произвольным именем аналогичным образом: команда < имя—файла Стандартный вывод может быть переназначен на файл с произвольным именем так, чтобы старое содержимое этого файла сохранялось, а то, что выводится, добавлялось в конец этого файла. Это может быть сделано сле- дующим образом: команда » имя—файла Если файл, на который переназначается стандартный вывод, еще не сущест- вует, то он будет создан автоматически. В ОС UNIX переназначить можно не только стандартный ввод-вывод. Переназначить можно также ввод-вывод для любого открытого файла, указание которого осуществляется с помощью пользовательского дескриптора файла. Для переназначения вывода вместо указанного с помощью пользовательского дескриптора файла на файл с произвольным именем нужно поступить следующим образом: команда >&2 имя—файла при этом указанный пользовательский дескриптор файла будет сдублирован и использован как стандартный вывод. В Приведенном выше примёре стан- дартный протокол переназначается на произвольный файл. Аналогично можно сдублировать и стандартный ввод: команда <&3 имя файла Приведенный ниже пример иллюстрирует более общую форму операций по переназначению ввода-вывода, когда указывается еще и пользователь- ский дескриптор файла, который должен быть создан и использован вместо стандартного вывода. Например, переназначить стандартный вывод и стан- дартный протокол на файл с произвольным именем можно следующим об- разом: команда 2>&1 имя—файла В ОС UNIX имеется также возможность закрыть стандартный ввод-вывод. Закрытие стандартного ввода осуществляется следующим образом: команда <&— Закрытие стандартного вывода осуществляется следующим образом: команда >&— В.2. ВЫПОЛНЕНИЕ КОМАНД В АСИНХРОННОМ РЕЖИМЕ Для того чтобы вызвать команду на выполнение в асинхронном режиме, необходимо после этой команды поместить символ &: команда& После вызова команды на выполнение в асинхронном режиме интерпретатор 282
команд shell выводит на терминал уникальный идентификатор процесса, в рамках которого выполняется эта команда. Информацию обо всех выпол- няющихся в данный момент процессах Вы можете получить с помощью ко- манды ps. В.З. КОНВЕЙЕРЫ Команды, которые осуществляют ввод только со стандартного ввода, а вывод производят только на стандартный вывод, называются фильтрами. Такие команды могут объединяться в конвейер следующим образом: команда! | команда2 В.4. ГЕНЕРАЦИЯ ИМЕН ФАЙЛОВ При генерации имен файлов символ » соответствует произвольной, в том числе и пустой, последовательности символов. Таким образом, в результате выполнения команды echo *.s на терминал будут выведены все имена файлов, содержащихся в текущем каталоге, последними двумя символами которых являются символы .s. При генерации имен файлов символ ? соответствует произвольному симво- лу. Таким образом, в результате выполнения команды echo prog.? на терминал будут выведены все имена файлов, содержащихся в текущем каталоге, состоящие из пяти или шести символов, первыми пятью симво- лами которых являются символы prog. . При генерации имен файлов по- следовательность символов, заключенная между символами [ и ] , соответст- вует любому символу из этой последовательности. Для сокращения записи используют символ — . Пара символов, разделенная символом — , соответст- вует (если она находится между символами [ и ] ) любому символу, лек- сикографически находящемуся между ними. Например, [a-z]* соответствует произвольному имени файла в текущем каталоге, начинаю- щемуся с любой строчной буквы латинского алфавита. Единственным ис- ключением при генерации имен файлов является то, что если имя файла на- чинается с символа . , то символ . должен быть указан в имени файла явно. В.5. ЭКРАНИРОВАНИЕ МЕТАСИМВОЛОВ Такие символы, как <, >, *, ?, I и &, являются метасимволами и имеют для интерпретатора команд shell специальный смысл. Снятие специального смысла с метасимвола называется экранированием метасимвола. Экраниро- вание метасимвола может быть осуществлено с помощью предшествующего метасимволу символа \, который, в свою очередь, является метасимволом. 283
Например, в результате выполнения команды echo \* на терминал будет выведен символ *, а в результате выполнения команды echo \\ на терминал будет выведен символ \. С помощью символа \ можно снять специальный смысл с символа newline, что позволяет вводить с терминала команды, длина которых превышает одну строку. Если в строке имеется несколько метасимволов, нуждающихся в экранировании, то вместо того, чтобы экранировать каждый из этих метасимволов по отдельности с по- мощью символа \, намного удобнее заключить всю такую строку между символами ' и ', что приводит к экранированию сразу всех метасимволов этой строки. Например, в результате выполнения команды echo ab'«\|*'cd на терминал будет выведено: ab»\|»cd Если заключить строку между символами " и ", то в этой строке будут экра- нированы все метасимволы, кроме $,', \, ". В.6. ПРОЦЕДУРЫ НА ЯЗЫКЕ ПРОГРАММИРОВАНИЯ shell Команды могут быть помещены в файл, который затем может быть пере- дан на выполнение интерпретатору команд shell. Файл, содержащий коман- ды, называется командным файлом, а содержимое командного файла назы- вается процедурой на языке программирования shell. На практике эти два понятия часто путают. Выполнить командный файл можно с помощью следующей команды: sh командный—файл [аргументы] Аргументы, указанные в командной строке для передачи в процедуру на языке программирования shell, доступны процедуре на языке програм- мирования shell как значения позиционных параметров $1, $2 и т. д. Число передаваемых в процедуру на языке программирования shell аргументов доступно в процедуре на языке программирования shell как значение пере- менной $# и включает в- себя имя командного файла, содержащего эту процедуру на языке программирования shell, которое доступно как значение позиционного параметра $0. Для подстановки последовательно всех аргу- ментов, передаваемых в процедуру на языке программирования shell, за ис- ключением имени командного файла, содержащего эту процедуру на языке программирования shell, необходимо воспользоваться значением перемен- ной $*, как это иллюстрируется следующим примером: sort -nr $• 284
В.7. СПЕЦИАЛЬНЫЕ ПЕРЕМЕННЫЕ, ПРЕДНАЗНАЧЕННЫЕ ДЛЯ ПЕРЕДАЧИ ПАРА- МЕТРОВ В ПРОЦЕДУРЫ НА ЯЗЫКЕ ПРОГРАММИРОВАНИЯ shell $0, $1, $2.. Позиционные параметры, соответствующие аргументам в командной строке; $0 — это имя командного файла, содержащего выполняющуюся в данный момент процедуру на языке программирования shell. — число позиционных параметров. $* — последовательная подстановка всех указанных в командной строке аргументов, за исключением имени командного файла, содержащего выпол- няющуюся в данный момент процедуру на языке программирования shell. $7 _ код завершения последней выполненной команды. Для большинства команд нулевое значение кода завершения служит индикатором успешного завершения их выполнения, а ненулевое значение кода завершения — инди- катором аварийного завершения их выполнения. Код завершения последней выполненной команды используется в управляющих конструкциях языка программирования shell, таких, например, как if, while и until. $$ — уникальный идентификатор процесса, в рамках которого выпол- няется интерпретатор команд shell. Обычно уникальный идентификатор процесса, в рамках которого выполняется интерпретатор команд shell, ис- пользуется для генерации уникальных имен временных файлов, как это ил- люстрируется следующим примером: cat filel file2 file3 > /tmp/t$4 rm /tmp/t$$ $! — номер процесса, в рамках которого выполняется последняя вызван- ная на выполнение в асинхронном режиме команда. $---значение флагов интерпретатора команд shell. В.8- ПЕРЕНАЗНАЧЕНИЕ СТАНДАРТНОГО ВВОДА НА ВЫПОЛНЯЕМУЮ В ДАННЫЙ МОМЕНТ ПРОЦЕДУРУ НА ЯЗЫКЕ ПРОГРАММИРОВАНИЯ shell Как следует из названия этого пункта, стандартный ввод может быть переназначен таким образом, что он будет осуществляться из тела выполня- емой в данный момент процедуры на языке программирования shell, что ил- люстрируется следующим примером: ed abc «# w # 285
Строки текста между символами «# и # поступят на стандартный ввод редактора текстов ed. Понятно, что символы «# обеспечивают переназначе- ние стандартного ввода на тело выполняемой в данный момент процедуры на языке программирования shell, а символ # служит ограничителем текста, по- ступающего на стандартный ввод из тела выполняемой в данный момент процедуры на языке программирования shell. Подстановка аргументов в текст, поступающий на стандартный ввод из тела выполняемой в данный момент процедуры на языке программирования shell, происходит до того, как этот текст поступит на стандартный ввод. Рассмотрим в качестве при- мера командный файл rmline: ed - $2 « # g/$1/d w # Тогда выполнение команды rmline def abc приведет к тому, что из файла abc будут удалены все строки, содержащие контекст def. Экранирование метасимволов в этом случае производится обычным образом, что иллюстрируется следующим примером: ed - $3« + 1,\$s/$1/$2/g w + Для того чтобы предотвратить подстановку аргументов в текст, посту- пающий на стандартный ввод из тела выполняемой в данный момент про- цедуры на языке программирования shell, наиболее эффективным яв- ляется использование приема, иллюстрируемого следующим примером: grep $1 «\@ В.9. ПЕРЕМЕННЫЕ В ЯЗЫКЕ ПРОГРАММИРОВАНИЯ shell В языке программирования shell применяются переменные типа строка символов. Имена переменных всегда должны начинаться со строчной или прописной буквы латинского алфавита и могут содержать строчные и про- писные буквы латинского алфавита, цифры и символ _. Переменные получают значения в результате присваивания, как это иллюстрируется следующим примером: mark =/usr/andy/bin/ flags = -О xyz = abc 286
Для того чтобы воспользоваться значением некоторой переменной, необходимо перед именем этой переменной употребить символ $, например echo $xyz R результате выполнения приведенной выше команды на терминал будет выведено следующее: abc Более общей формой использования значения некоторой переменной является форма ${имя—переменной} которая позволяет компоновать имена файлов частично из готовых заго- товок, частично из значений переменных, как это иллюстрируется сле- дующим примером: mv a.out ${ mark} newcmd В результате выполнения приведенной выше команды файл, доступный ранее по относительному полному имени a.out, в текущем каталоге станет доступен по абсолютному полному имени /usr/andy/bin/newcmd Следующие переменные имеют для интерпретатора команд shell спе- циальный смысл. НОМЕ — значение переменной НОМЕ (т. е. $НОМЕ) используется в том случае, когда Вы вызываете на выполнение команду cd без аргумента. Обычно значение переменной НОМЕ устанавливается в результате выпол- нения командного файла .profile. Как правило, переменная НОМЕ содержит имя основного каталога пользователя. IFS — значение переменной IFS (т. е. $|FS) — это последовательность символов, которые используются в качестве разделителей в командной строке. Обычно такими символами являются следующие символы: и, tab и newline. MAIL — если интерпретатор команд shell выполняется в интерактивном режиме, то каждый раз перед выводом на терминал промптера интерпре- татор команд shell проверяет содержимое файла, имя которого является значением переменной MAIL (т. е. $MAIL), и если содержимое этого файла изменилось с момента последнего ввода из него, то перед тем как вывести на терминал промптер, интерпретатор команд shell выводит на терминал сообщение You have mail PATH — значением переменной PATH (т. е. $РАТН) является последова- тельность абсолютных полных имен каталогов, разделенных с помощью символа :, в соответствии с которой осуществляется поиск файла в случае указания собственно имени этого файла. Обычно значение переменной 287
PATH обеспечивает поиск файла в следующей последовательности: текущий каталог, каталог /bin, каталог /usr/bin. Вы можете произвольно изменять последовательность поиска файла, присваивая соответствующее значение переменной PATH, как это показано в следующем примере: PATH = :/usr/andy/bin:/bin:/usr/bin PS1 — значение переменной PS1 {т. е. $PS1) задает основной промптер интерпретатора команд shell. Как правило, основным промптером интер- претатора команд shell является символ $• PS2 — значение переменной PS2 (т. е. $PS2) задает вид дополнительного промптера интерпретатора команд shell. Как правило, дополнительным промптером интерпретатора команд shell является символ>. В.Ю. УПРАВЛЕНИЕ ПОСЛЕДОВАТЕЛЬНОСТЬЮ ДЕЙСТВИЙ В ЯЗЫКЕ ПРОГРАММИ- РОВАНИЯ shell В операторе цикла for и операторе выбора case для принятия решения о дальнейших действиях используются аргументы, передаваемые в процеду- ру на языке программирования shell, а в операторе цикла while, опера- торе цикла until и условном операторе if для принятия решения о даль- нейших действиях используются коды завершения команд. Значительная часть команд в ОС UNIX вырабатывает код завершения только для этой цели. Операторы управления последовательностью действий могут быть использованы как в процедурах на языке программирования shell, так и в интерактивном режиме. В последнем случае основной промптер интер- претатора команд shell, задаваемый значением переменной PS1, заменяет- ся на дополнительный промптер, вид которого задается 'значением пере- менной PS2. Это напоминает Вам о том, что Вы еще не закончили выпол- нение команды. При описании операторов управления последовательностью действий квадратные скобки указывают на необязательность частей опера- торов, заключенных в них. ОПЕРАТОР ЦИКЛА for Оператор цикла for используется для организации циклов в соответ- ствии с указанными аргументами, при этом для каждого указанного аргумента тело цикла выполняется один раз. В обобщенном виде оператор цикла for выглядит следующим образом: for имя [in список—значений] do список—команд done Если часть этого оператора, начинающаяся с ключевого слова in, опуще- на, то это означает in $*, как это иллюстрируется следующим примером: for i do grep $i *.c done 288
. ОПЕРАТОР ВЫБОРА case Оператор выбора case используется для организации ветвления по мно- гим направлениям в зависимости от значения указанного аргумента. В обобщенном виде оператор выбора case выглядит следующим образом: case имя in шаблон) список—команд;; шаблон) список—команд;; esac УСЛОВНЫЙ ОПЕРАТОР if В обобщенном виде условный оператор if выглядит следующим обра- зом: if список—команд then список—команд [e!if список—команд] [else список- команд] fi Условный оператор if, как правило, используется вместе с оператором test, что иллюстрируется следующим примером: if test -f andy then команда! else команда? fi Условный оператор if часто используется также следующим образом: if команда! then команда? fi Приведенная выше форма использования условного оператора if упот- ребляется настолько часто, чтодля нее имеется сокращенная форма записи: команда! && команда? При использовании этой формы записи "команда?" будет выполнена в том, и только в том случае, если "команда!" завершит свое выполнение ус- пешно. Кроме того, имеется аналогичная конструкция: команда! || команда? При использовании этой конструкции "команда?" будет выполнена в том, и только в том случае, если "команда!" завершит свое выполнение аварийно. Последняя конструкция может быть выражена с помощью условного опера- 10 Зак 1165 289
тора if, если часть его, начинающаяся со служебного слова then, является фик- тивной, т. е. не приводит к выполнению каких-либо действий: if команда! then true else команда2 fi ОПЕРАТОРЫ цикла while и until В обобщенном виде оператор цикла while выглядит следующим обра- зом: while список—команд do список—команд done Условием выполнения тела цикла оператора цикла while является то, что код завершения последней команды из списка команд, находящегося в строке, начинающейся со служебного слова while, равен нулю. Условие выполнения тела цикла может быть инвертировано, если слу- жебное слово while заменить на служебное слово until. Приведенный ниже пример иллюстрирует использование оператора цикла until: until test —f lock do sleep 30; done последующие—команды Тело цикла приведенного выше оператора цикла until будет выполнять- ся до тех пор, пока существует файл lock. В.11. ПОДСТАНОВКА В ВЫПОЛНЯЕМУЮ В ДАННЫЙ МОМЕНТ ПРОЦЕДУРУ НА ЯЗЫКЕ ПРОГРАММИРОВАНИЯ shell РЕЗУЛЬТАТОВ ВЫПОЛНЕНИЯ НЕКОТОРОЙ КОМАНДЫ, ВЫВОДИМЫХ ЭТОЙ КОМАНДОЙ НА СТАНДАРТНЫЙ ВЫВОД Как следует из названия пункта, результаты выполнения некоторой команды, выводимые этой командой на стандартный вывод, могут быть использованы в процедуре на языке программирования shell, при этом их использование аналогично использованию значений переменных. Для того чтобы использовать в процедуре на языке программирования shell результаты выполнения некоторой команды, выводимые этой командой на стандартный вывод, необходимо заключить эту команду между символами ' и ' следующим образом: path = 'pwd' Выполнение командной строки, приведенной выше, эквивалентно выпол- нению командной строки path =/usr/andy/unixbook 290
в том случае, если текущим каталогом является каталог /usr/andy/unixbook. В.12. ГРАММАТИКА языка программирования shell пусто: слово: последовательность символов, не содержащая символа имя: последовательность символов, содержащая строчные и про- писные буквы латинского афавита, цифры и символ _ и на- чинающаяся со строчной буквы латинского алфавита цифра: 01 23456789 айтем: слово ввод-вывод имя-значение простая-команда айтем простая-команда айтем команда: простая-команда (список-команд) {список-команд] for имя do список-команд done for имя 1пслово ... do список-команд done while список-команд do список-команд done until список-команд do список-команд done case слово in case—часть .. . esac if список-команд then список-команд else-часть fi конвейер: команда конвейер I команда андор: конвейер андор && конвейер андор || конвейер список-команд: андор список-команд; СПИСОК-КОМЭНД& список-команд; андор список-команд& андор ввод-вывод: > файл < файл » слово « слово файл: слово& цифра &- case-часть: шаблон) список-команд;; 291
шаблон: слово шаблон(слово else-часть: elif список-команд then список-команд else-часть else список-команд пусто МЕТАСИМВОЛЫ И ЗАРЕЗЕРВИРОВАННЫЕ СЛОВА а) синтаксические 1 && II символ конвейера символ andf символ orf & разделитель команд разделитель ветвей признак вызова команды на выполнение в асинхронном ре- жиме ( ) группирование команд переназначение стандартного ввода переназначение стандартного ввода на выполняемую в дан- ный момент процедуру на языке программирования shell переназначение стандартного вывода переназначение стандартного вывода для добавления б) генерация имен файлов * произвольная, в том числе и пустая, последовательность сим- волов произвольный символ любой из перечисленных символов в) подстановки г г подстановка значения переменной подстановка результатов выполнения команды, выводимых этой командой на стандартный вывод г} экранирование метасимволов t t экранирование непосредственно следующего метасимвола экранирование всех метасимволов, кроме символа ' t/ tt экранирование всех метасимволов, кроме символов $, ', \, " д) зарезервированные слова if then else elif fi case in esac for while until do done О 292
ПРИЛОЖЕНИЕ Г. СТАНДАРТНЫЕ БИБЛИОТЕКИ Ниже приведен список библиотечных функций, входящих в состав библи- отек, поставляемых вместе со стандартной версией 7 ОС UNIX. Для каждой функции в приведенном ниже списке указаны также флаг, который необхо- димо употреблять при компоновке программ для включения в них данной функции (если употребление флага необходимо} и номер страниц этой книги, на которой Вы сможете найти краткое описание данной функции. Особо от- мечены функции, входящие в состав стандартной библиотеки ввода-вывода (SIO), функции,обеспечивающие работу с мультиплексными файлами (МРХ), и функции, служащие для выполнения системных вызовов (SYS). Функции, для которых не указан тип,считаются по умолчанию возвращающими целые значения. Функция Флаг для компоновки Номер страницы Примечания abort () 311 abs(x) 311 access(name. mode) 322 SYS acct(file) 323 SYS doable acos(x) 310 a(arm(seconds) 323 SYS arc(x, y, xO, yO, x1, y1) - Iplot 321 asctime(tm) 312 double asin(x) - Im 310 assert (expression) 311 double atan(x) - Im 310 double atan2(x) - Im 310 double atof(nptr) 312 atoi(nptr) 312 long atol(nptr) 312 attach(i, xd) 300 MPX brk(addr) 323 SYS cabs(z) - Im 310 calloc(nelem, elsize) 316 double ceil(x) -Im 311 chan(xd) 300 MPX chdir(dirname) 323 SYS chmod(name, mode) 324 SYS chown(name, owner, group) 323 SYS chroot(dirname) 000 SYS circle(x, y, r) -Iplot 321 ckill(i, xd, signal) 300 MPX clearerr(stream) 301 SIO 10В з,1к и(Г> 293
close(file) 323 SYS closepl () - I plot 322 connected, cd, end) 300 MPX cont(x, y) - I plot 321 double cos(x) - Im 310 double cosh(x) - Im 310 creat(name, mode) 324 SYS crypt(key, salt) 312 char •ctime(clock) 312 detach(i, xd) 300 MPX dup(fildes) 324 SYS dup2(fildes, fildes2) 324 SYS ecvt(value, ndigit, decpt, sign) 313 extern edata 314 encrypt(block, edflag) 312 extern end 314 endgrentQ 315 endpwent() 315 erase() - I plot 320 extern etext 314 execl(name,arg0,arg1,...,argn,0) 324 SYS execle(name,arg0,arg1,...,argn,envp) 324 SYS execv(name, argv) 324 SYS execve(name, argv, envp) 324 SYS exit(status) 325 SYS double exp(x) - Im 311 extract(i, xd) 300 MPX double fabs(x) - Im 311 fclose(stream) 309 SIO fcvt(value, ndigit, decpt, sign) 313 FILE *fdopen(fildes, type) 300 SIO feof(stream) 301 SIO ferror(stream) 301 SIO fflush(stream) 309 SIO fgetc(stream) 302 SIO char *fgets(s, n; stream) 302 SIO fileno(stream) 301 SIO double floor(x) - Im 311 FILE *fopen(filename, type) 300 SIO fork() 325 SYS fprintf(stream, format [,arg ...]) 304 SIO fputc(c, stream) 303 SIO fputs(s, stream) 303 SIO fread(prt, sizeof(-ptr), nitems, stream) 301 SIO 294
free(ptr) 316 FILE »freopen(filename, type, stream) 300 SIO double frexp(value, eptr) 314 fscanf (stream, format [,ptr...]) 305 SIO fseek (stream, offset, ptrname) 308 SIO fstat(fildes, buf) 328 SYS long ftell(stream) 308 SIO ftime(tp) 328 SYS fwrite(ptr, sizeof(*ptr), nitems, stream) 301 SIO gcd(a, b, c) -Imp 320 char *gcvt(value, ndigit, buf) 313 getc (stream) 302 SIO getchar() 302 SIO getegid() 325 SYS char *getenv(name) 314 geteuid() 325 SYS getgid() 325 SYS struct group »getgrent() 314 struct group »getgrid(gid) 314 struct group *getgrnam(name) 314 char <>getlogin() 315 char *getpass(prompt) 315 getpid() 325 SYS getpw(uid, buf) 315 struct passwd *getpwent() 315 struct passwd *getpwnam(name) 315 struct passwd *getpwuid(uid) 315 char *gets(s) 302 SIO getuid() 325 SYS getw(stream) 302 SIO struct tm *gmtime(clock) 312 gtty(fildes, argp) 299 SYS double hypot(x, y) -Im 310 char *index(s, c) 318 ioctl(fildes, request, argp) 325 SYS isalnumfc) 313 isalpha(c) 313 isascii(c) 313 isatty(fildes) 319 iscntrl(c) 313 isdigit(c) 313 islower(c) 313 isprint(c) 313 ispunct(c) 313 10В 295
isspace(c) 313 isupper(c) 313 mint *itom(n) - Imp 320 double jO(x) - Im 309 double jl (x) - Im 309 double jn(n, x) -Im 309 join(fd, xd) 300 MPX kill(pid, sig) 325 SYS I3tol(lp, cp, n) 315 label (s) - (plot 320 double ldexp(value, exp) 314 Iine(x1, yl, x2, y2) - (plot 321 linemod(s) - Iplot 321 Iink(name1, name2) 326 SYS , struct tm *localtime(clock) 312 lock(flag) 299 SYS double log(x) - Im 311 double Iog10(x) - Im 311 longjmp(env, val) 318 long Iseekfftldes, offset, whence) 326 SYS Itol3(cp, Ip, n) 315 madd(a, b. c) - Imp 320 char •malloc(size) 316 mdivfa, b, q, r) - Imp 320 min(a) - Imp 320 mknodfname, mode, addr) 326 SYS char •mktemp(template) 316 double modffvalue, iptr) 314 monitor(lowpc,highpc,buffer,bufsize,nfunc) 316 mount(special, name, rwflag) 326 SYS mout(a) - Imp 320 move(x, y) - Iplot 321 mpx(name, access) 300 MPX mpxcall(cmd, vec) 300 SYS, MPX msqrtfa, b, r) - Imp 320 msub(a, b, c) - Imp 320 mult(a, b, c) — Imp 320 nice(incr) 326 SYS nlistffilename, nl) 317 npgrpfi, xd, pgrp) 300 MPX open(name, mode) 327 SYS openplQ - Iplot 320 pause(} 327 SYS pclose(stream) 307 SIO 296
perror(s) 317 phys(segreg, size, physadr) 299 SYS pipe(fildes) 327 SYS point(x, y) - Iplot 321 FILE •popen(command, type) 307 SIO pow(a, b, m, c) - Imp 320 double pow(x, y) - Im 311 printf(format [,arg .,.]) 304 SIO profil(buff, bufsiz, offset, scale) 327 SYS ptrace(request, pid, addr, data) 327 SYS putc(c, stream) 303 SIO putchar(c) 303 SIO puts(s) 303 SIO putw(w, stream) 303 SIO qsort(base, nel, width, compar) 316 rand() 316 read(fildes, buffer, nbytes) 327 SYS char *realloc(ptr, size) 316 rewind(stream) 308 SIO char'*rindex(s, c) 318 rpow(a, b, c) - Imp 320 char *sbrk(incr) 323 SYS scant (format [,ptr ...]) 305 SIO sdiv(a, n, q, r) - Imp 320 setbuf(stream, buf) 308 SIO setgid(grd) 327 SYS setgrent() 315 setjrhp(env) 318 setkey(key) 312 setpwentf) 315 setuid(uid) 327 SYS («signal(sig, func))() 328 SYS double sin(x) - Im 310 double sinh(x) - Im 310 sleep(seconds) 318 spacefxO, yO, x1, y1) - Iplot 321 sprintffs, format [,arg ...]) 304 SIO double sqrt(x) -Im 311 srand (seed) 316 sscanf(s, format [,arg ...]) 305 SIO statfname, buf) 328 SYS stime(tp) 328 SYS char *strcat(s1, s2) 318 strcmpfsl, s2) 318 297
char *strcpy(s1, s2) 318 strlen(s) 318 char *strncat(s1, s2, n) 318 strncmp(s1, s2, n) 318 char •strncpy(s1, s2, n) 318 sttyffildes, argp) 299 SYS swab(from, to, nbytes) 319 sync() 328 SYS system(string) 319 char »sys errlist[]; 317 sys nerr; 317 double tan(x) - Im 310 double tanh(x) - Im 310 time(tloc) 328 SYS times(buffer) 329 SYS char *timezone(zone, dst) 312 char *ttyname(fildes) 319 ttyslot() 319 umask(complmode) 329 SYS ungetc(c, stream) 302 SIO unlink(name) 329 SYS utime(file, timep) 329 SYS wait(status) 330 SYS write(fildes, buffer, nbytes) 330 SYS double yO(x) -Im 309 double y1 (x) -Im 309 double yn(n, x) -Im 309 exit(status) 325 SYS sleep(seconds) 318 space(xO, yO, x1, y1) -Iplot 321 sprintffs, format [,arg ...]) 304 SIO double sqrt(x) - Im 311 srand(seed) 316 sscanf(s, format [,arg ...]) 305 SIO statfname, buf) 328 SYS stime(tp) 328 SYS char *strcat(s1, s2) 318 strcmp(s1, s2) 318 char *strcpy(s1, s2) 318 strlen(s) 318 char *strncat(s1, s2, n) 318 strncmp(s1, s2, n) 318 char ♦strncpy(s1, s2, n) 318 sttyffildes, argp) 299 SYS 298
swab(from, to, pbytes) 319 sync() 328 SYS system (string) 319 char *sys errlist[]; 317 sys nerr; 317 double tan(x) - Im 310 double tanh(x) - Im 310 time(tloc) 328 SYS times(buffer) 329 SYS char ♦timezone(zone, dst) 312 char *ttyname(fildes) 319 ttyslotQ 319 umask(complmode) 329 SYS ungetc(c, stream) 302 SIO unlink(name) 329 SYS utime(file, timep) 329 SYS wait(status) 330 SYS write(fildes, buffer, nbytes) 300 SYS double yO(x) - Im 309 double y1 (x) - Im 309 double yn(n, x) - Im 309 exit(status) 325 SYS Г.1. УСТАРЕВШИЕ СИСТЕМНЫЕ ВЫЗОВЫ Некоторые из системных вызовов, включенных в приведенный выше спи- сок, либо имеют историческое происхождение, либо специфичны для ЭВМ се- мейства PDP-11. Поэтому эти системные вызовы, конечно, отсутствуют в ря- де версий ОС UNIX и подобных ей операционных систем. Мы включили их в приведенный выше список исключительно из соображений полноты. Системные вызовы gtty, stty представляют собой первоначальную форму запроса и установки режимов работы терминалов. Позднее они были замене- ны системным вызовом ioctl, которым и надлежит пользоваться при разра- ботке нового программного обеспечения. Системный вызов lock позволяет зафиксировать некоторый процесс в опе- ративной памяти, т.е. запретить своппинг этого процесса. Вообще говоря, использование этого системного вызова нарушает нормальную работу тех ком- понент ядра ОС UNIX, которые ответственны за управление памятью, и может привести к возникновению тупиковых ситуаций. Системный вызов phys обеспечивает для процесса возможность осуществ- лять непосредственный доступ к физической памяти. Оба этих вызова потен- циально представляют большую опасность и поэтому, скорее всего, отсутст- вуют в большинстве версий ОС UNIX и подобных ей операционных систем. 299
Устаревший системный вызов tell функционально эквивалентен вызову Iseek (f i Ides, OL, 11 Легко заметить, что функцией этого системного вызова является возвраще- ние в качестве своего значения текущего положения указателя чтения-записи. Г.2. МУЛЬТИПЛЕКСНЫЕ ФАЙЛЫ Мультиплексные файлы представляют собой средство, включенное в ОС UNIX пока еще в качестве эксперимента. Это означает, что вероятность нали- чия в нем ошибок весьма высока. Если в используемой Вами версии ОС UNIX имеется возможность использовать мультиплексные файлы, то мы настоятель- но рекомендуем Вам перед тем, как использовать эту возможность, тщательно изучить МРХ (2) и попытаться получить необходимые консультации у Вашего гуру. Г 3. СТАНДАРТНАЯ БИБЛИОТЕКА ВВОДА-ВЫВОДА Любая программа, в которой используются функции, входящие в состав стандартной библиотеки ввода-вывода, должна содержать в качестве одной из первых строку ^include <stdio.h> Г.3.1. Открытие файлов как потоков данных FILE *fopen{filename, type) FILE »freopen(filename, type, stream) FILE «fdopen(fildes, type) char •filename, •type; FILE «stream; Библиотечная функция fopen обеспечивает в результате своего выполнения открытие как потока данных файла, специфицированного с помощью аргу- мента filename, и возвращает в качестве своего значения указатель на файл. С помощью аргумента type специфицируется тип доступа к открываемому как поток данных файлу следующим образом: г — открытие файла для ввода информации из него, w — открытие файла для вывода информации в него, а — открытие файла для добавления информации в него. Библиотечная функция freopen обеспечивает переназначение ввода из неко- торого уже открытого как поток данных файла или вывода в некоторый уже открытый как поток данных файл на файл, специфицированный с помощью аргумента filename. В качестве своего значения библиотечная функция freopen возвращает указатель на файл, для которого было осуществлено переназна- чение ввода или вывода. Наиболее часто библиотечная функция freopen ис- пользуется для переназначения ввода на stdin и переназначения вывода на stdout или на stderr. Библиотечная функция fdopen позволяет указать для 300
открываемого как поток данных файла не имя файла, а пользовательский дескриптор файла, полученный в качестве значения-одного из следующих си- стемных вызовов: open, dup, creat, pipe. Г.3.2. Получение статусной информации о файлах, открытых как потоки данных feof(stream) ferror(stream) clearerr(stream) fileno(stream) FILE j>stream; Библиотечная функция feof возвращает ненулевое значение ("истина") в том случае, если достигнут конец файла, и нулевое значение ("ложь") в про- тивном случае. Библиотечная функция terror возвращает ненулевое значение ("истина") в том случае, если в ходе выполнения операций ввода-вывода возникла ошибка, и нулевое значение ("ложь") в противном случае. Если при выполнении операций ввода-вывода возникла ошибка, то до тех пор пока она не будет сброшена или файл, открытый как поток данных, не будет закрыт, библиотечная функция terror будет возвращать ненулевое значение. Сбросить ошибку можно с помощью библиотечной функции clearerr. Если после обращения к библиотечной функции clearrer сразу же обратиться к биб- лиотечной функции terror, то последняя возвратит нулевое значение. Библио- течная функция fileno возвращает в качестве своего значения пользователь- ский дескриптор файла, соответствующего специфицированному указателю на файл (не путайте понятия пользовательского дескриптора файла и указа- теля на файл). Использование пользовательских дескрипторов файлов, по-видимому, не будет иметь очень широкого распространения. Пользователь- ские дескрипторы файлов применяются в тех случаях, когда требуется избе- жать использования дополнительного уровня буферизации, обеспечиваемого при использовании библиотечных функций, входящих в состав стандартной библиотеки ввода-вывода. Г. 3.3. Операции ввода-вывода fread(ptr, sizeof(*ptr), nitems, stream) fwrite(ptr, sizeof(*ptr), nitems, stream) FILE «stream; Библиотечные функции tread и fwrite используются для проведения опера- ций ввода-вывода с использованием дополнительного уровня буферизации. Операция ввода-вывода производится над порцией информации, местополо- жение которой в оперативной памяти специфицированно с помощью аргу- мента ptr, а размер специфицирован с помощью аргумента nitems. При этом размер этой порции информации задается в виде числа объектов, тип которых соответствует типу объектов, для указания на которые используется аргумент 301
ptr. В качестве своего значения библиотечные функции fread и fwrite возвра- щают число реально введенных/выведенных объектов. int getc(stream) int getchar() int fgetc(stream) int getw(stream) FILE «stream; Библиотечные функции getc, getchar, fgetc возвращают в качестве своего значения введенный символ, а бибилиотечная функция getw -- введенное сло- во. Библиотечная функция getchar () эквивалентна библиотечной функции getc(stdin) и является по сути сокращенной формой записи последней. Библи- отечная функция fgetc функционально эквивалентна библиотечной функции getc. Отличие состоит в том, что библиотечная функция fgetc реализована в виде функции, к которой производится обращение, а библиотечная функция getc — в виде макроса. Поэтому следующий пример является абсолютно кор- ректным: FILE «fp; fgetc( fp+ + ); Все эти библиотечные функции возвращают целые значения. Если встречен конец файла или в ходе проведения операций ввода возникла ошибка, все эти библиотечные функции возвращают в качестве своего значения целую константу EOF. char *gets(s) char «fgetsfs, n, stream) char «s; FILE «stream; Библиотечная функция getc обеспечивает вводе stdin (стандартного ввода) строки символов, завершаемой символом newline в оперативную память. Местоположение введенной строки символов в оперативной памяти специфи- цируется с помощью s. В качестве своего значения библиотечная функция getc возвращает указатель на местоположение введенной строки символов в оперативной памяти. При вводе строки с помощью библиотечной функции getc символ newline, завершающий вводимую строку символов, заменяется на нуль. Библиотечная функция fgetc обеспечивает ввод строки символов из произвольного файла, открытого как поток данных. Ввод будет закончен либо после ввода п—1 символа, среди которых не было ни одного символа newline, либо после ввода символа newline. Строка символов, введенная с помощью этой библиотечной функции, будет дополнена нулем. В качестве своего значения библиотечная функция fgetc возвращает указатель на место- положение введенной строки символов в оперативной памяти. Если будет до- стигнут конец файла или в ходе операции ввода возникла ошибка, каждая из этих двух библиотечных функций в качестве своего значения возвращает целую константу NULL. ungetc(c, stream) char c; FILE «stream;
Библиотечная функция ungetc обеспечивает возможность "затолкнуть" обратно в файл, открытый как поток данных для ввода, последний введенный из него символ, который может быть потом опять введен с помощью библи- отечной функции getc. Для того чтобы воспользоваться этой библиотечной функцией, необходимо ввести по крайней мере один символ из файла, от- крытого как поток данных для ввода. В случае невозможности "затолкнуть" символ обратно в файл, открытый как поток данных для ввода, библиотечная функция ungetc возвращает в качестве своего значения целую константу EOF. Всякая попытка воспользоваться этой библиотечной функцией после того, как был встречен конец файла, обречена на провал. int putc(c. stream), int putchar(c) int fputc (c, stream) int putw(w, stream) char c; unsigned w; FILE * stream; Перечисленные выше библиотечные функции выводят в файл, открытый как поток данных для вывода, и возвращают в качестве своего значения байт (putc, putchar, fputc) или слово (putw). Библиотечная функция putchar(c) эквивалентна библиотечной функции putc(c, stdout). Библиотечная функция putc функционально эквивалентна библиотечной функции fputc. Отличие со- стоит в том, что библиотечная функция fputc реализована в виде функции, к которой производится обращение, а библиотечная функция putc — в виде макроса. Поэтому следующий пример является абсолютно корректным: char «аср; FILE «fp; fputc( »аср+ +, fp); Необходимо отметить, что если вывод производится на stdout (стандартный вывод), то дополнительный уровень буферизации не используется. Если в ходе проведения операций вывода возникла ошибка, все эти библиотечные функции возвращают в качестве своего значения целую константу EOF. По- скольку целая константа EOF может быть выведена с помощью библиотечной функции putw, то в этом случае для выявления ошибки необходимо восполь- зоваться библиотечной функцией ferror. puts(s) fputs(s, stream) char «s; FILE «stream; Библиотечная функция puts выводит на стандартный вывод (stdout) стро- ку символов, завершаемую нулем, заменяя при этом нуль на символ newline. Местоположение подлежащей выводу строки символов в оперативной памяти специфицируется с помощью s. Библиотечная функция tputs обеспечивает вывод завершаемой нулем строки символов в произвольный файл, открытый как поток данных для вывода, заменяя при этом нуль на символ newline. Местоположение подлежащей выводу строки символов в оперативной памяти специфицируется с помощью S. 303
Г.3.4. Форматный ввод-вывод printf(format [, arg ...]) fprintf(stream, format [, arg ...]) sprintf(s, format [, arg ...]) char *s, «format; FILE «stream; Каждая из перечисленных выше библиотечных функций производит пре- образование и вывод своих аргументов (arg . . Спецификации преобразо- вания и формата специфицируются с помощью format. Библиотечная функция printf обеспечивает вывод на стандартный вывод (stdout). Библиотечная функция fprintf обеспечивает вывод в произвольный файл, открытый как поток данных для вывода. Библиотечная функция sprintf обеспечивает вывод в строку, местоположение которой в оперативной памяти специфицируется с помощью s, добавляя при этом в эту строку нуль в качестве последнего вы- водимого символа. Строка символов, содержащая спецификации преобразования и формата, анализируется этими библиотечными функциями посимвольно. Содержимое этой строки символов выводится в том же самом виде, в каком оно в ней со- держится до тех пор, пока в ней не будет встречен символ %. Символ % инди- цирует начало очередной спецификации преобразования и формата, которая определяет, каким образом будет выведен очередной аргумент (arg). Ниже перечислены простейшие спецификации преобразования и формата задавае- мые с помощью символа, который следует за символом %: d = аргумент трактуется как целое число и выводится в десятичном виде; о = аргумент трактуется как целое число и выводится в восьмеричном виде; х = аргумент трактуется как целое число и выводится в шестнадцатерич- ном виде; с = аргумент трактуется как символ, который и выводится; s = аргумент трактуется как указатель на строку символов, завершаемую нулем, которая и выводится; f = аргумент трактуется как число с плавающей точкой или число с пла- вающей точкой с удвоенной точностью и выводится в следующем виде: [-] ddd.ddd; е = аргумент трактуется как число с плавающей точкой или число с пла- вающей точкой с удвоенной точностью и выводится в следующем виде: [-] d.ddde [ +- ] dd; d = аргумент трактуется как число с плавающей точкой или число с пла- вающей точкой с удвоенной точностью и выводится либо в виде %d, либо в’ виде %f, либо в виде %е, в зависимости от того, какой из них будет иметь минимальное число знаков, сохраняя при этом требуемую точность. Ведущие нули при выводе опускаются; и = аргумент трактуется как целое беззнаковое число и выводится в деся- тичном виде. 304
Для того чтобы вывести символ %, необходимо в строке, содержащей спе- цификации преобразования и формата, записать %%. Между символом % и спецификацией преобразования и формата дополни- тельно могут быть включены следующие символы: символ —, указывающий на необходимость выравнивания по левому краю поля формата; строка, состоящая из цифр, которая специфицирует минимальное число знаков в поле формата. Если полученное после преобразования число содер- жит меньшее число знаков, чем задано в спецификации числа знаков в поле формата, то оно будет дополнено заполнителем слева (а если был использован символ —, то справа). Обычно заполнителем является символ <_i. Если строка, которая специфицирует минимальное число знаков в поле формата, начинает- ся с символа 0, то заполнителем будет являться не символ о, а символ 0; символ ., отделяющий строку, состоящую из цифр, которая специфициру- ет число знаков в поле формата от другой строки, состоящей из цифр; строка, состоящая из цифр, которая следует за символом . и специфициру- ет число знаков после точки для чисел с плавающей точкой и чисел с плаваю- щей точкой с удвоенной точностью или максимальное число знаков для строк символов; символ I, который указывает на то, что следующая за ним спецификация преобразования и формата d, о, х, или и должна быть применена к числу типа long. В этом случае можно использовать также спецификации преобразования и формата в виде прописных, а не строчных букв латинского алфавита, так как %ld эквивалентно %D. scanf(format [, ptr ...]) fscanf(stream, format [, ptr ...]) sscanffs, stream [, ptr ...]) char *s, format; FILE •stream: Эти библиотечные функции противоположны по смыслу библиотечным функциям printf, fprintf и sprintf соответственно. Эти библиотечные функции обеспечивают форматный ввод и проведение соответствующих преобразова- ний. Символы вводятся из соответствующего источника и преобразуются в соответствии со спецификациями преобразования и формата, специфициро- ванными с помощью format. Результаты преобразования заносятся в опера- тивную память, а их местоположение специфицируется с помощью оставшихся аргументов, которые должны быть указателями на переменные соответст- вующего типа. Библиотечная функция scanf обеспечивает ввод со стандартно- го ввода (stdin). Библиотечная функция fscanf обеспечивает ввод из произволь- ного файла, открытого как поток данных для ввода. Библиотечная функция sscanf обеспечивает ввод из строки символов, завершающейся символом 0, ме- стоположение которой в оперативной памяти специфицируется с помощью s. Строка, содержащая спецификации преобразования и формата, может содержать: 305
Символ lj, символ tab и символ newline, которые могут, если это необхо- димо, соответствовать очередным вводимым символам или игнорироваться. Одиночные символы (кроме символа %), которые соответствуют очеред- ным вводимым символам. Спецификации преобразования и формата, состоящие из символа %, за которым следует символ, определяющий соотвэтствующую спецификацию преобразования и формата, между которыми могут быть числа, специфици- рующие максимальное число знаков поля формата и/или символ •, указыва- ющий на необходимость игнорирования данного поля формата. Символ I, указывающий на то, что следующая за ним спецификация преоб- разования и формата d, о или х должна быть применена к числу типа long, при этом соответствующий указатель должен быть указателем на число типа long,а не указателем на целое число. Абсолютно аналогично символ 1 может предшествовать спецификациям преобразования и формата е и f, указывая на то, что соответствующая спецификация преобразования и формата должна быть применена к числу с плавающей точкой с удвоенной точностью, а не к числу с плавающей точкой. В этом случае можно также использовать специ- фикации преобразования и формата в виде прописных, а не строчных букв латинского алфавита, так как %lf эквивалентно %F. Символ h, указывающий на то, что следующая за ним спецификация пре- образования и формата d, о или х должна быть применена к числу типа short. При этом соответствующий указатель должен быть указателем на число типа short, а не указателем на целое число. Впрочем, возможно, что на использу- емой Вами ЭВМ целые числа не отличаются от чисел типа short. Поля формата представляют собой строки символов, отличных от симво- лов <-j, tab и newline, завершаемых одним из этих символов по достижении максимального числа знаков соответствующего поля формата, если такое было специфицировано. Спецификации преобразования и формата относятся к последовательным полям формата и аргумента, являющимся указателями на объекты соответствующих типов, в которые должны быть помещены результаты преобразования. При указании в качестве спецификации преоб- разования и формата символа * соответствующее поле формата просто про- пускается. Поскольку символ newline трактуется библиотечными функ- циями scanf, fscanf и sscanf так же, как символ <_i, то с помощью этих функций можно осуществлять, вообще говоря, ввод произвольного числа строк. Ниже перечислены используемые для библиотечных функций scanf, fscanf и sscanf спецификации преобразования и формата, аналогичные спецификаци- ям преобразования и формата, используемым для библиотечных функций printf, fprintf и sprintf; d — предполагается, что будет введено десятичное целое число; соответст- вующий аргумент должен быть указателем на целую переменную; о — предполагается, что будет введено восьмеричное целое число; соот- ветствующий аргумент должен быть указателем на целую переменную; х — предполагается, что будет введено шестнадцатеричное целое число; со- ответствующий аргумент должен быть указателем на целую переменную; 306
с — предполагается, что будет введен один символ; соответствующий ар- гумент должен быть указателем на символьную переменную. Обычно символы newline и tab не игнорируются, поэтому для того, чтобы быть уверенным, что будет введен первый символ, отличный от вышеперечисленных, необхо- димо использовать спецификацию преобразования и формата %1s; s — предполагается, что будет введена строка символов, завершаемая сим- волом ц_> или символом newline; соответствующий аргумент должен быть указателем на массив достаточного размера для того, чтобы вместить введен- ную строку символов и добавляемый после ее ввода нуль. f — предполагается, что будет введено число с плавающей точкой; соот- ветствующий аргумент должен быть указателем на переменную с плавающей точкой. В качестве синонима f можно использовать е. Предполагаемый фор- мат вводимого числа является комбинацией форматов, являющихся резуль- татом применения спецификаций преобразования и формата f и е в библио- течных функциях printf, fprintf и sprintf: необязательный знак минус, строка цифр, возможно содержащая символ . , за которой следует необязательная экспоненциальная часть, состоящая из символа е или Е, необязательного сим- вола + или — и строки цифр; [ — предполагается, что будет введена строка символов, не завершаемая символом i_> или символами tab, newline. Вслед за символом [ должна следовать последовательность символов, образующих вводимую строку символов, завершаемая символом ] . Если первым после символа [ является символ Л, то ограничителем строки будет служить любой ид, символов, вхо- дящий в набор символов, размещенных между символами [ и ] (кроме.сим- вола Л). В противном случае ограничителем строки будет служить любой из символов, не входящий в набор символов, размещенный между символами [ и ] Соответствующий аргумент должен быть таким же, как и для случая %s; % — предполагается, что будет введен символ %. В качестве своего значения библиотечные функции scanf, fscanf и sscanf возвращают число успешно введенных полей формата, а в случае конца файла — целую константу EOF. Г. 3.5. Коммуникации между процессами (каналы) FILE »popen(cmd, type) pclose(stream) char «cmd, «type; FILE «stream; Библиотечная функция popen в результате своего выполнения осуществля- ет вызов на выполнение командной (в смысле интерпретатора команд shell) строки, специфицированной с помощью cmd, соединяя процесс, в котором встретилось обращение к этой библиотечной функции, со стандартным вво- дом-выводом вызываемой на выполнение командной строки. Направление обмена специфицируется с помощью type следующим образом: w — вывод, г — ввод. В качестве своего значения библиотечная функция рореп возвращает 307
указатель на файл, открытый как поток данных, который впоследствии будет использоваться для вывода на стандартный ввод вызываемой на выполнение командной строки или для ввода со стандартного ввода вызываемой на вы- полнение командной строки. Библиотечная функция pclose используется для закрытия файла, открытого как поток данных с помощью библиотечной функции рореп, обеспечивая ожидание завершения вызванной на выполнение командной строки и возвращая код ее завершения в качестве своего значе- ния. При попытке закрыть с помощью библиотечной функции pclose файл, который не был открыт как поток данных с помощью библиотечной функции рореп, библиотечная функция pclose возвращает в качестве своего значе- ния —1. Г.3.6. Управление файлами, открытыми как потоки данных fseek(stream, offset, whence) long ftell(stream) rewind(stream) FILE «stream; long offset; Библиотечная функция fseek функционально эквивалентна системному вызову Iseek. В результате выполнения библиотечной функции fseek указатель чтения-записи файла, открытого как поток данных, будет перемещен Соот- ветствующим образом. Величина смещения (в байтах) указателя чтения-за- писи специфицируется с помощью offset, а база смещения указателя чтения- записи — с помощью whence следующим образом: 0 — смещение от начала файла, 1 — смещение от текущего положения указателя чтения-записи, 2 — смещение от конца файла. После обращения к библиотечной функции fseek результаты выполнения библиотечной функции ungetc будут утеряны. Библи- отечная функция ftell возвращает в качестве своего значения текущее поло- жение указателя чтения-записи файла, открытого как поток данных, вычис- ленное в байтах от начала файла. Библиотечная функция rewind функциональ- но эквивалентна библиотечной функции fseek(stream, (long)O, 0) setbuf(stream, buf) FILE «stream; char «buf; Буфера для файлов, открытых как потоки данных, как правило, выделя- ются динамически в результате обращения к библиотечной функции malloc. Библиотечная функция setbuf применяется для статического определения буфера для файла, открытого как поток данных, и используется после от- крытия файла как потока данных, но до проведения операций ввода-вывода с этим файлом. В результате выполнения библиотечной функции setbuf в качестве буфера будет использоваться массив, указатель на который специ- фицируется с помощью buf. Если указатель, специфицируемый с помощью buf, не указывает ни на какой массив (т. е. содержит нуль), то ввод-вывод будет проводиться без буферизации. 308
Г.З 7. Закрытие файлов, открытых как потоки данных fclose(stream) ft lush (stream) FILE «stream; Библиотечная функция fclose обеспечивает в результате своего выполнения закрытие файла, открытого как поток данных, предварительно уничтожая содержимое всех ассоциированных с ним буферов. Библиотечная функция fflush в результате своего выполнения обеспечивает проведение отложенного вывода. Для того чтобы закрыть файл, открытый как поток данных для вы- вода, необходимо сначала обратиться к библиотечной функции fflush, а затем уже к библиотечной функции fclose. Г.4. БИБЛИОТЕЧНЫЕ ФУНКЦИИ ДЛЯ РЕШЕНИЯ РАЗЛИЧНЫХ МАТЕМАТИЧЕСКИХ ЗАДАЧ Библиотечные функции для решения различных математических задач на- ходятся в библиотеке /Iib/libm.a. При компоновке программ, написанных на языке программирования Фортран, в которых используются эти библиотеч- ные функции, их подключение производится автоматически. В программах, написанных на языке программирования Си, в которых используются эти библиотечные функции, их подключение производится в том случае, если для компоновки был использован флаг —Im. Любая программа, в которой ис- пользуются эти библиотечные функции, должна в качестве одной из своих первых строк содержать строку “include <math.h> б результате включения препроцессором в исходный текст программы файла Ajsr/incJude/math.h обеспечивается корректность описания типов всех функ- ций для решения различных математических задач. Г. 4.1. Вычисление функций Бесселя double jO(x) double х; double j1 (x) double x; double jn(n, x) double n, x; double yO(x) double x; double y1 (x) double x; double yn(x) double n, x; Перечисленные выше библиотечные функции обеспечивают В результате своего выполнения вычисление значении функций Бесселя первого и второго родов для действительных значений аргумента и целых значений порядка. В случае отрицательности аргумента (аргументов) библиотечные функции уО, у1 и уп возвращают в качестве своего значения очень большое по абсолют- ной величине отрицательное число и устанавливают код ошибки EDOM в еггпо. 309
Г. 4.2. Вычисление тригонометрических функций double sin(x) double х; double cos(x) double x; double tan(x) double x; double asin(x) double x; double acos(x) double x; double atan(x) double x; double atan2(x,y) double x,y; Для библиотечных функций sin, cosh tan предполагается, что значение ар- гумента указано в радианах, а в качестве своих значений они возвращают значения соответствующих тригонометрических функций. Значение, возвра- щаемое библиотечной функцией tan в случае, если значение указанного при обращении к ней аргумента соответствует точке сингулярности тригономет- рической функции tg, будет очень большим, а в errno будет установлен код ошибки ERANGE. Библиотечная функция asin возвращает в качестве своего значения арксинус указанного при обращении к ней аргумента. Диапазон зна- чений, возвращаемых библиотечной функцией asin, включает значения от —тУ2 до +я/2. Библиотечная функция acos возвращает в качестве своего значения арккосинус указанного при обращении к ней аргумента. Диапазон значений, возвращаемых библиотечной функцией acos, включает значения от 0 до2тт. Библиотечная функция atan возвращает в качестве своего значения арктан- генс указанного при обращении к ней аргумента. Диапазон значений, возвра- щаемых библиотечной функцией atan, включает значения от —тг/2 до +тг/2. Библиотечная функция atan? возвращает в качестве своего значения арктан- генс указанного при обращении к ней аргумента. Диапазон значений, возвра- щаемых библиотечной функцией atan2, включает значения от —я до +я . Г. 4.3. Вычисление гиперболических функций double sinh(x) double х; double cosh(x) double x; double tanh(x) double x; Перечисленные выше библиотечные функции возвращают в качестве своих значений значения соответствующих гиперболических функций для действи- тельных значений аргумента. Г.4.4. Вычисление расстояния между двумя точками double hypot(x, у) double х; double cabs(z) struct { double x, y; } z; Каждая из этих библиотечных функций возвращает в качестве своего зна чения sqrt (х*х + у*у). 310
Г.4.5. Вычисление различных математических функций Библиотечная функция ceil возвращает в качестве своего значения наи- меньшее целое число, не меньшее чем х. double ceil(x) double х; Библиотечная функция ехр возвращает в качестве своего значения ех. double ехр(х); double х; Библиотечная функция fabs возвращает в качестве своего значения абсо- лютное значение х. double fabs(x) double х; Библиотечная функция floor возвращает в качестве своего значения наи- большее целое число, не большее чем х. double floor(x) double х; Библиотечная функция log возвращает в качестве своего значения In х. Библиотечная функция loglO возвращает в качестве своего значения logi0x. double 1од(х) double х; double 1од10(х) double х; Библиотечная функция pow возвращает в качестве своего значения хЛ double pow(x, у); double х, у; Библиотечная функция sqrt возвращает в качестве своего значения у/х. double sqrt(x) double х; Г.5. СМЕСЬ abort() Библиотечная функция abort является машинно-зависимой библиотечной функцией. На ЭВМ семейства PDP-11 обращение к библиотечной функции abort приводит к выполнению машинной команды ЮТ, в результате чего производится дамп содержимого оперативной памяти, а выполнение процесса, обратившегося к библиотечной функции abort, завершается. Не стоит поль- зоваться библиотечной функцией abort как средством отладки программ, по- скольку в ОС UNIX имеются более удобные средства отладки программ, например отладчик abd. abs(x) Библиотечная функция abs возвращает в качестве своего значения абсо- лютное значение целого числа х. ^include <assert.h> assert(expression) 311
Библиотечная функция assert представляет собой макрос, обеспечивающий проверку и подтверждение того, что выражение, специфицированное с по- мощью аргумента expression, принимает ненулевое значение ("истина"). В случае, если выражение, специфицированное с помощью аргумента expression, принимает нулевое значение ("ложь"), выполняется системный вызов exit, которому предшествует вывод на стандартный вывод диагностического со- общения. Выполнение действий, которые производятся в результате обра- щения к библиотечной функции assert, можно предотвратить, если восполь- зоваться флагом— DNDEBUG команды сс. double at of (pt г) char «ptr; atoi(ptr) char «ptr; long atol(ptr) char «ptr; Перечисленные выше библиотечные функции обеспечивают преобразование строки символов, специфицируемой с помощью аргумента ptr, в число с плава- ющей точкой (atof), целое число (atoi) или число типа long (atol). Лидирую- щие символы с., и tab игнорируются, а необязательный символ — распознается. char *crypt(key, salt) char «key, «salt; setkey(key) char «key; encrypt(block, edflag) char «block; Библиотечная функция crypt, используемая для шифровки пароля, осно- вана на алгоритме DES (Data Encryption Standard), принятом в качестве стан- дарта Национальным бюро стандартов. Строка символов, которую необхо- димо подвергнуть шифрованию, специфицируется с помощью аргумента key, а строка из двух символов, выбираемых из набора [a—zA—Z0—9], исполь- зуемая для внесения элемента случайности в алгоритм шифровки — с помо- щью аргумента salt. В качестве своего значения библиотечная функция crypt возвращает указатель на зашифрованную строку. Библиотечные функции setkey и encrypt обеспечивают возможность прямого доступа к алгоритму DES и предназначены для использования их теми пользователями, которые предполагают создание и использование своих собственных схем для крипто- графической защиты информации. 'include <time.h> char «ctime(clock) long «clock; struct tm «localtime(clock) long «clock; struct tm «gmtime(clock) long «clock; char «asctime(tm) struct tm »tm; char *timezone(zone, dst) Библиотечная функция ctime обеспечивает в результате своего выполнения преобразование времени из формата, возвращаемого системным вызовом time, в строку символов следующего вида: Моп Маг 8 18:33:58 GMT 1982\п Все поля этой строки символов имеют постоянный размер, поэтому вывести 312
из программы на языке программирования Си только месяц и дату можно следующим образом: long clock; time(&clock); printf('%.6s\n', &ctime(&clock)[4]); Библиотечные функции local time, gm time, asctime и timezone обеспечивают возможность проведения преобразований времени, полученного в формате, возвращаемом системным вызовом time в различные форматы. 'include <ctype.h> isalpha(c), isupper(c), islower(c), isdigit(c), isalnum(c), isspace(c), ispunct(c), isprint(c), iscntrl(c), isascii(c) Перечисленные выше библиотечные функции представляют собой макросы, обеспечивающие проверку того, чем является символ, специфицированный в качестве их аргумента. В качестве своего значения каждая из этих библио- течных функций возвращает ненулевое значение, если результат проверки есть “истина", и нулевое значение, если результат проверки есть "ложь". isalpha (с) — "истина", если с — строчная или прописная буква латинс- кого алфавита; isupper (с) islower (с) isdigit (с) isalnum(c) — "истина", если с — прописная буква латинского алфавита; — "истина", если с — строчная буква латинского алфавита; — "истина", если с — цифра; — "истина", если с — либо строчная или прописная буква латинского алфавита, либо цифра; isspace(c) — "истина", если с — один из следующих символов: tab, return, newline или formfeed; ispunct (с) — "истина", если с имеет код в диапозоне или от 041 до 057, или от 072 до 100, или от 133 до 140, или от 173 до 176; isprint (с) — "истина", если с — печатный символ (т. е. его код нахо- дится в диапазоне от 040 до ,76) ; iscntrl(c) — "истина", если с — непечатный символ Гт. е. его код нахо- дится в диапазоне от 000 до 037 или равен 177); isascii (с) — "истина", если с — символ кода ASCII (т, е. его код на- ходится в диапазоне от 000 до 177). char *ecvt(value, ndigit, decpt, sign) char *fcvt(value, ndigit, decpt, sign) char *gcvt(value, ndigit, buf) double value; int ndigit «decpt, «sign; char «buf; Перечисленные выше библиотечные функции обеспечивают в результате своего выполнения преобразование численных значений в строки символов, завершаемые символом 0. I 1 Зак 1165 313
extern end extern etext extern edata Перечисленные выше имена не соответствуют никаким библиотечным функциям. Им соответствуют фиксированные адреса в выполняемой про- грамме: etext — первый адрес после конца разделяемого сегмента чисто- го кода, т. е. первый адрес, принадлежащий сегменту инициализированных данных; edata — первый адрес после конца сегмента инициализированных данных, т. е. первый адрес, принадлежащий сегменту не- инициализированных данных; end — первый адрес после конца сегмента неинициализирован- ных данных; double frexp(num, eptr) double num; int «eptr; double Idexpfnum, exp) double num; double modffnum, iptr) double num, *iptr; Перечисленные выше библиотечные функции обеспечивают в результате своего выполнения разбиение числа с плавающей точкой на мантиссу и поря-, док. Библиотечная функция frexp возвращает в качестве своего значения мантиссу числа с плавающей точкой, специфицированного с помощью num, как число с плавающей точкой с удвоенной точностью, находящееся в диапа- зоне от -1 до +1, и порядок этого числа, используя указатель на целую пере- менную eptr. Библиотечная функция Idexp возвращает в качестве своего зна- чения num*2**exp как число с плавающей точкой с двойной точностью. Биб- лиотечная функция modf возвращает в качестве своего значения дробную часть числа с плавающей точкой, специфицированного с помощью аргумента num, как число с плавающей точкой с удвоенной точностью и целую часть этого числа, используя для этого указатель на целую переменную iptr. char *getenv(name) char «name; Описание контекста процесса состоит из строк вида name = value (см. environ (5)). Библиотечная функция getenv обеспечивает поиск среди строк описания контекста процесса строки, для которой ее часть, стоящая слева от символа =, совпадает с аргументом этой библиотечной функции и возвращает в качестве своего значения указатель на завершаемую нулем часть найденной ею строки описания контекста процесса, стоящую справа от символа =. Если поиск дал отрицательный результат, то в качестве своего значения библиотеч- ная функция getenv возвращает целую константу NULL. ^include <grp.h> struct group >getgrent() struct group «getgrgid(gid) int gid; struct group *getgrnam(name) char «name; 314
setgrentf) endgrentf) Перечисленные выше библиотечные функции предназначены для проведе- ния различных манипуляций над файлом /etc/group. При возникновении каких-либо ошибок каждая из этих библиотечных функций возвращает в качестве своего значения целую константу NULL. char <>getlogin() Библиотечная функция getlogin в результате своего выполнения возвраща- ет в качестве своего значения указатель на имя, под которым зарегистрирован в ОС UNIX пользователь, от чьего имени выполняется процесс, в котором было произведено обращение к этой библиотечной функции. char «getpass (prompt) char «prompt; Библиотечная функция getpass осуществляет ввод строки co стандартного ввода после того, как она предварительно выведет на стандартный вывод за- вершаемую нулем строку символов, специфицируемую с помощью prompt, и отключит режим "эхо". В качестве своего значения библиотечная функция getpass возвращает указатель на завершаемую символом 0 строку символов, максимальная длина которой не превышает восьми символов. getpwfuid, but) char «but; Библиотечная функция getpw осуществляет поиск в файле /etc/passwd строки, содержащей идентификатор пользователя, специфицируемый с помощью аргумента uid, и, если такая строка будет найдена, возвращает в качестве своего значения нуль, заполняя при этом массив, специфицирован- ный с помощью указателя buf, найденной строкой, завершаемой нулем, а если такая строка не будет найдена, возвращает ненулевое значение. ^include <pwd.h> struct passwd >getpwent() struct passwd «getpwuid(uid) int uid; struct passwd »getpwnam(name) char «name; set p went () endpwent() Перечисленные выше библиотечные функции предназначены для проведе- ния различных манипуляций над файлом /etc/passwd. При возникновении каких-либо ошибок каждая из этих библиотечных функций возвращает в ка- честве своего значения целую константу NULL. I3tol(lp, ср, n) long *lp; char -ср; Itol3(cp, Ip, n) char *cp; long «ср; Библиотечная функция 13tol обеспечивает в результате своего выполнения проведение преобразования последовательности трехбайтовых целых чисел, специфицированной с помощью аргумента ср, в последовательность длинных IJ 315
целых чисел, специфицируемую спомощью аргумента !р. Библиотечная функ- ция Ио13обеспечивает в результате своего выполнения проведение обратного преобразования. Эти библиотечные функции используются программами icheck и dcheck. char «malioc(size) unsigned size; free(ptr) char »ptr; char »realloc(ptr, size) char *ptr; unsigned size; char «calloc (nelem, elsize) unsigned nelem, elsize; Перечисленные выше библиотечные функции реализуют несложную систе- му управления памятью. Библиотечная функция malloc возвращает в качестве своего значения указатель на область памяти, начинающуюся на границе слова. Размер этой области в байтах специфицирован с помощью аргумента size. Библиотечная функция free обеспечивает в результате своего выполнения ос- вобождение выделенной ранее с помощью библиотечной функции malloc об- ласти памяти, специфицируемой с помощью аргумента ptr. Библиотечная функция realloc обеспечивает в результате своего выполнения изменение раз- мера области памяти, выделенной ранее с помощью библиотечной функции malloc. Библиотечная функция calloc возвращает в качестве своего значения указатель на область памяти, размер которой достаточен для помещения туда специфицированного с помощью аргумента nelem числа объектов, размер каждого из которых специфицирован с помощью аргумента elsize, или целую константу NULL, если область памяти соответствующего размера отсутствует. Как правило, библиотечная функция calloc используется следующим образом: ptr = calloc(n, sizeof(object)); char *mktemp( tempi ate) char «template; Библиотечная функция mktemp в результате своего выполнения обеспечи- вает генерацию уникального имени временного файла, используя для этого идентификатор процесса, в котором встретилось обращение к этой библио- течной функции, и шаблон имени, задаваемый строкой символов, шесть по- следних из которых являются символами х. Эта строка специфицируется с помощью аргумента template, например: char -аср; аср = mktemp(7tmp/andyxxxxxx'); monitorflowpc, highpc, buffer, bufsize, nfunc) int (*lowpc)(), (*highpc)(); short buffer[]; Библиотечная функция monitor обеспечивает в результате своего выполне- ния получение профиля времени выполнения программы на языке програм- мирования Си. Использование флага —р команды сс приводит к автоматичес- кому включению в программу на языке программирования Си обращений к библиотечной функции monitor, аргументе! которой задаются по умолчанию. 316
include <a.out.h> nlist(filename, nl) char «filename; struct nlist nl[]; Библиотечная функция nlist в результате своего выполнения извлекает информацию из таблицы символов, хранящейся в файле, содержащем выпол- няемую программу или объектный код некоторой программы. Имя этого файла специфицировано с помощью аргумента filename. Извлекаемая из таб- лицы символов информация помещается в массив структур, специфицирован- ный с помощью аргумента nl. Этот массив структур содержит имена перемен- ных, их типы и значения. Для всех встреченных в таблице символов имен переменных производится заполнение соответствующих им полей, в которых хранятся типы переменных и значения переменных в указанном массиве структур. perror(s) char »s; int sys_nerr; char »sys__errlistf]; Библиотечная функция реггог в результате своего выполнения выводит на стандартный протокол текст сообщения об ошибке, номер которой хранится в переменной errno, предваряя его строкой символов, завершаемой нулем, которая специфицирована с помощью аргумента s. Текст сообщения об ошиб- ке будет найден с помощью элемента массива указателей на строки символов— sys—errlist, номер которого равен текущему значению переменной errno. Суммарное число сообщений об ошибках хранится в целой переменной sys— nerr. qsort(base, nel, width, compar) char «base; int (*compar)(); Библиотечная функция qsort представляет собой реализацию алгоритма быстрой сортировки. Пользователь с помощью указателя compar должен специфицировать функцию, обеспечивающую проведение сравнения двух эле- ментов сортируемого массива и использующую в качестве своих аргументов указатели на элементы сортируемого массива. В качестве своего значения функция, специфицируемая с помощью указателя compar, должна возвращать целое число, меньшее нуля, равное нулю или большее нуля для случаев, когда первый аргумент этой функции меньше второго, равен второму или больше второго соответственно. rand() srand(seed) Библиотечная функция rand возвращает в качестве своего значения псевдо- случайное число в диапазоне от 0 до 28158. Начальное значение генератора псевдослучайных чисел устанавливается с помощью библиотечной функции srand, в качестве аргумента которой указывается произвольное число. 317
^include <setjmp.h> setjmp(env) jmp____but env; longjmp(env) jmp___buf env; Перечисленные выше библиотечные функции используются для проведе- ния нелокальных безусловных переходов в программах на языке програм- мирования Си. Использование этих библиотечных функций оказывается очень удобным для реализации обработки ошибок, возникающих в функциях с большим уровнем вложенности. sleep(seconds) unsigned seconds; Библиотечная функция sleep в результате своего выполнения приводит к приостановке выполнения процесса, в котором встретилось обращение к ней, на число секунд, специфицированное с помощью аргумента seconds. Функции для работы со строками символов char *strcat(s1, s2) char <>strncat(s1, s2, n) strcmp(s1. s2) strncmp(s1, s2, n) •strcpy(s1, s2) char •strncpy(s1, s2, n) strlen(s) char «indexes, c) char <>rindex{s, c) char c, *s, *s1, *s2; Перечисленные выше библиотечные функции обеспечивают работу со стро- ками символов, завершаемыми нулем. 8 ходе выполнения этих библиотеч- ных функций при создании и конкатенации строк не производится никаких проверок на переполнение (т. е. контроль за переполнением возложен на пользователя). Библиотечная функция strcat в результате своего выполне- ния осуществляет конкатенацию строки, специфицированной с помощью аргумента si, и строки, специфицированной с помощью аргумента s2, а результирующую строку размещает на месте первой из этих строк. Биб- лиотечная функция stmcat в результате своего выполнения производит ана- логичные действия, используя при этом только п первых символов строки, специфицированной с помощью аргумента s2. Библиотечная функция strcmp обеспечивает сравнение двух строк, специфицированных с помощью аргу- ментов si и s2, и возвращает в качестве своего значения целое число,мень- шее нуля, равное нулю или большее нуля для случаев, когда первая строка лек- сически меньше второй, лексически равна второй и лексически больше второй соответственно. Библиотечная функция stmcmp обеспечивает проведение ана- логичных действий, используя при этом только п первых символов строки, специфицированной с помощью аргумента s2. Библиотечная функция strcpy в результате своего выполнения копирует строку, специфицированную с 318
помощью аргумента s2, в строку, специфицированную с помощью аргумента si. Библиотечная функция stmcpy обеспечивает проведение аналогичных действий, используя при этом только п первых символов строки, специфи- цированной с помощью аргумента s2. Библиотечная функция strlenB качест- ве своего значения возвращает число символов, содержащихся в строке, специфицированной с помощью аргумента s. Библиотечная функция index в качестве своего значения возвращает порядковый номер символа, являюще- гося первым вхождением символа, специфицированного с помощью аргумен- та с, в строку, специфицированную с помощью аргумента s. Библиотечная функция rindex в качестве своего значения возвращает порядковый номер символа, являющегося последним вхождением символа, специфицирован- ного с помощью аргумента с, в строку, специфицированную с помощью аргумента s. swab(from, to, nbytes) char *from, *to; Библиотечная функция swab используется при обмене информацией меж- ду ЭВМ с различным порядком следования байт. В результате выполнения библиотечной функции swab производится копирование массива байт, спе- цифицированного с помощью аргумента from в массив байт, специфициро- ванный с помощью to, в ходе которого четные и нечетные байты меняются местами. Число копируемых байт специфицируется с помощью nbytes. system(cmd____string) char «cmd__string; Библиотечная функция system в результате своего выполнения обеспе- чивает выполнение командной строки (в смысле интерпретатора команд shell), специфицированной с помощью аргумента cmd_string. В качестве своего значения библиотечная функция system возвращает код завершения выполненной командной строки. В случае, если выполнение специфицирован- ной командной строки невозможно, библиотечная функция system возвраща- ет в качестве своего значения 127. char *ttyname(fildes) isatty(fildes) ttyslot() Перечисленные выше библиотечные функции используются для определе- ния имени терминала, ассоциированного с процессом, в котором встретилось обращение к этим библиотечным функциям. Библиотечная функция ttyname в качестве своего значения возвращает указатель на завершаемую нулем стро- ку символов, содержащую абсолютное полное имя файла, соответствующего специфицированному с помощью аргумента ftides файлу, если он соответст- вует терминалу, или целую константу NULL, если этот файл не соответствует терминалу. Библиотечная функция isatty возвращает в качестве своего значе- ния ненулевое значение, если файл, специфицированный с помощью аргумен- та fildes, соответствует терминалу, и нулевое значение в противном случае. 319
Библиотечная функция ttyslot возвращает в качестве своего значения поряд- ковый номер строки в файле /etc/ttys, соответствующий терминалу, ассоци- ированному с процессом, и нуль, если таковую не удается найти. Г.6. СПЕЦИАЛЬНЫЕ БИБЛИОТЕКИ Г.6-1. Библиотечные функции, реализующие арифметические операции неограниченной точности над целыми числами Флаг компоновки:—Imp typedef struct { int len; short *val; } mint; maddfa, b, c) msub(a, b, c) mult(a, b, c) mdivfa, b, q, r) min(a) mout(a) pow(a, b, m, c) gcd(a, b, c) rpow(a, b, c) msqrt(a, b, r) mint »a, »b, *c, *m, *q, *r; sdiv(a, n, q, r) mint »a, »q; short *r; mint *itom(n) Целые числа, обрабатываемые с помощью перечисленных выше библиотеч- ных функций, должны храниться в переменных, имеющих тип mint. Указа- тели на переменные типа mint могут быть инициализированы с помощью библиотечной функции itom, обеспечивающей установку начального значения, специфицированного с помощью аргумента п. Г.6.2. Библиотечные функции, обеспечивающие интерфейс с устройствами отображения графической информации орепр|() Обращение к библиотечной функции openpl обязательно должно предшест- вовать открытию любых других файлов. erase() Обращение к библиотечной функции erase обеспечивает переход к выводу нового кадра или новой страницы. label(s) char *s; Обращение к библиотечной функции label приводит к выводу завершаемой нулем или символом newline строки символов, специфицированной с по- мощью аргумента s, начиная от текущей точки. 320
Iine(x1, y1, x2, y2) Библиотечная функция line обеспечивает в результате своего выполнения проведение прямой линии от точки с координатами (х1, у1) до точки с коор- динатами (х2, у2). circle(x, у, г) Библиотечная функция circle обеспечивает в результате своего выполне- ния проведение окружности радиусом г с центром в точке с координатами (х, у). агс(х, у,х1, у1, х2, у2) Библиотечная функция аге обеспечивает в результате своего выполнения проведение дуги с центром в точке с координатами (х, у,) начинающейся в точке с координатами (х1, у 1) и заканчивающейся в точке с координатами (х2, у2). move(x, у) Библиотечная функция move в результате своего выполнения обеспечивает установку точки с координатами (х,у) в качестве текущей точки. cont(x, у) Библиотечная функция cont обеспечивает в результате своего выполнения проведение прямой линии от текущей точки до точки с координатами (х,у). point(x, у) Библиотечная функция point в результате своего выполнения обеспечива- ет опуркание пера в точке с координатами (х,у). linemod(s) char *s; Библиотечная функция linemod обеспечивает в результате своего выполне- ния установку типа проводимых линий, окружностей и дуг. Существуют сле- дующие типы линий, окружностей и дуг: dotted, solid, longdashed, shortdashed и dotdashed — точечные, сплошные, длинные, пунктирные, короткие пунктир- ные, штрихпунктирные. Тип проводимых линий, окружностей и дуг специфи- руется с помощью аргумента s, являющегося указателем на строку символов, завершаемую нулем и содержащую соответствующее название. Использова- ние библиотечной функции linemod имеет смысл только тогда, когда аппарат- ные средства позволяют проводить несплошные линии, окружности и дуги. space(xO, уО, х1, у1) Библиотечная функция space позволяет определить поле, в котором будет осуществляться последующая работа, следующим образом: точка с координа- тами (хО, уО) определяет нижний левый угол поля, а точка с координатами (х1,у1) — верхний правый угол поля. 321
closepl () Библиотечная функция closepl обеспечивает проведение отложенного вы- вода на устройство отображения графической информации. Флаги компоновки: — Iplot не зависящие от устройства библиотечные функции, предлагающие работу со стандартным выводом, используемые plot (1); — 1300 библиотечные функции для терминала GSI 300; — 13005 библиотечные функции для терминала GSI 3005; — 1450 библиотечные функции для терминала DASI; — 14014 библиотечные функции для терминала Tektronix 4014 и сов- местимых с ним устройств. ПРИЛОЖЕНИЕ Д. СИСТЕМНЫЕ ВЫЗОВЫ В настоящем приложении описываются наиболее важные системные вызовы ОС UNIX. Для каждого системного вызова указываются его имя, тип возвращаемого им значения (по умолчанию — целое), список аргумен- тов и их типы. Например, запись long Iseek(fildes,offset,whence) long offset; означает, что системный вызов Iseek возвращает в качестве своего значения целое типа long. При этом аргумент offset также должен быть целым типа long, а остальные аргументы (fildes и whence) должны быть целыми. Заме- тим, что аргументы, описанные как char »ххх, могут быть заменены сим- вольными строками (многие пользователи почему-то не используют эту возможность). При описании некоторых системных вызовов будет исполь- зована директива препроцессора языка программирования Си # include. Ее применение означает, что в программу, в которой имеется обращение к соответствующему системному вызову, необходимо включить текст указан- ного в директиве файла. Как правило, такой файл будет содержать описа- ния переменных, необходимые для обращения к этому системному вызову. Представленное описание системных вызовов не является ни подробным, ни полным и поэтому не может быть использовано в качестве учебного по- собия. Данное приложение можно использовать скорее как краткий спра- вочник, а в случае возникновения каких-либо сомнений следует обратиться к разд. 2 Руководства. СИСТЕМНЫЕ ВЫЗОВЫ ОС UNIX access (name, mode)char «name; Системный вызов acess выполняет проверку разрешения на доступ к фай- лу, имя которого специфицировано с помощью аргумента name для реальных идентификаторов пользователя и группы. Значение аргумента mode опреде- ляет режим доступа следующим образом: 4 — по чтению, 2 — по записи, 1 — по 322
выполнению. Указанные значения могут комбинироваться с помощью ло- гической операции ИЛИ. Если аргумент mode равен 0, то будет выполнена проверка полного имени файла. Значение, возвращаемое этим системным вызовом, равно 0 при нормальном завершении и —1 при аварийном завер- шении. acct(file)char «file; Системный вызов acct включает/выключает режим сбора статистической информации, которая будет поступать в файл, имя которого задается с по- мощью аргумента file. Если аргумент file равен 0, то режим сбора будет выключен. Системный вызов acct доступен только привилегированному пользователю. alarm(seconds)un$igned seconds; Системный вызов alarm информирует ядро ОС UNIX о необходимости послать данному процессу сигнал побудки через указанное с помощью аргумента seconds число секунд. Значение, возвращаемое этим системным вызовом, представляет собой число секунд, заданное при предыдущем об- ращении к этому системному вызову. char «brk(addr) char «sbrk(incr) Системный вызов brk обеспечивает расширение размера процесса до адреса, заданного с помощью аргумента addr, а системный вызов stork — на величи- ну, указанную с помощью аргумента incr (приращение в байтах}. Значение, возвращаемое каждым из этих системных вызовов, равно 0 при нормальном завершении и —1 при аварийном завершении. chdir(dirname)char «dirname: Системный вызов сИгПгобеспечивает смену текущего каталога на каталоге именем, указанным с помощью аргумента dirname. Значение, возвращаемое этим системным вызовом, равно 0 при нормальном завершении и —1 при аварийном завершении. chown(name,owner,group)char «пате: Системный вызов chown устанавливает файлу с заданным с помощью аргу- мента name именем идентификаторы пользователя и группы, заданные в ар- гументах owner и group. Системный вызов chown доступен только привиле- гированному пользователю. Значение, возвращаемое этим системным вы- зовом, равно 0 при нормальном завершении и —1 при аварийном заверше- нии. close(fildes) Системный вызов close закрывает файл, пользовательский дескриптор которого задан с помощью аргумента tildes. Значение, возвращаемое этим 323
системным вызовом, равно 0 при нормальном завершении и —1 при аварий- ном завершении. creat(name,mode)char «name; Системный вызов creat создает и открывает на запись файл с указанным с помощью аргумента name именем и кодом защиты файла, заданным с по- мощью аргумента mode. При нормальном завершении значение, возвраща- емое этим системным вызовом, представляет собой пользовательский деск- риптор файла, при аварийном завершении оно равно —1. chmod(name,mode)char «пате; Системный вызов chmod изменяет код защиты файла, имя которого задано с помощью аргумента пате. Новый код защиты файла задается с помощью аргумента mode, детальное описание возможных значений кода защиты представлено в Руководстве. Изменить код защиты файла может либо его владелец, либо привилегированный пользователь. Значение, возвращаемое этим системным вызовом, равно 0 при нормальном завершении и —1 при аварийном завершении. dup(old) dup2(old,new) Системные вызовы dup и dup2 создают копию пользовательского дескрипто- ра файла, заданного с помощью аргумента old. Системный вызов dupe каче- стве своего значения возвращает первый свободный пользовательский дес- криптор файла. Системный вызов dup2 закрывает, если он был открыт, файл с пользовательским дескриптором файла, заданным с помощью аргу- мента new, и возвращает в качестве своего значения этот пользовательский дескриптор файла. Оба этих системных вызова в случае ошибки возвращают в качестве своего значения —1. execle(name,arg0,arg1 ,....,0,envp)char *name,*argO, «argl.... ... »envp[]; execve(name,argv,envp)char *name,«argv[],*envp[]; Системные вызовы execle и execve осуществляют загрузку и передают управление программе, содержащейся в файле, имя которого задано с по- мощью аргумента name. В качестве аргумента системному вызову execle передается также список указателей на строки символов (argO, argl, . . .). При обращении к системному вызову execve передается массив указателей argv. В обоих случаях последний указатель должен быть равен 0. Аргумент envp, называемый контекстом процесса, также представляет собой массив указателей, последний элемент которого равен 0. Более подробная информа- ция о системных вызовах execle и execve приведена в разд. 2 Руководства. Значение, возвращаемое каждым из этих системных вызовов в случае ошиб- ки, равно —1, в случае нормального завершения возврата не происходит. 324
exit(arg) Системный вызов exit завершает выполнение процесса. При этом выпол- няется очистка некоторых областей памяти, например буферов ввода-выво- да. Обращение к системному вызову _exit предотвратит очистку этих об- ластей. Восемь младших битов аргумента передаются процессору-предку. Возврата не происходит. fork() Системный вызов fork создает копию текущего процесса. Процесс-пре- док в качестве значения, возвращаемого этим системным вызовом, получит идентификатор процесса-потомка. Значение, возвращаемое этим системным вызовом в процесс-потомок, равно 0. Если создать процесс невозможно, то значение, возвращаемое этим системным вызовом, равно —1. qetpid Значение, возвращаемое системным вызовом getpid, представляет собой идентификатор текущего процесса. getuid() getgid() geteuid() getegid() Значения, возвращаемые перечисленными выше системными вызовами, представляют собой реальные или действующие идентификаторы пользова- теля или группы. ^include <sgtty.h> ioctlffildes,request,argp)struct sgtty «argp; Системный вызов ioctl используется для различных целей. Подробности см. в Руководстве. При аварийном завершении значение, возвращаемое этим си- стемным вызовом, равно —1, а при нормальном завершении 0. kill (pid ,sig) Системный вызов kill посылает сигнал, номер которого задан с помощью аргумента sig, процессу, идентификатор процесса которого задан с помощью аргумента pid. Как посылающий, так и получающий сигнал процессы должны иметь одинаковый действующий идентификатор пользователя, за исключе- нием того случая, когда сигнал посылается из процесса привилегированного пользователя. Если аргумент pid равен 0, то сигнал посылается всем процес- сам, входящим в группу текущего процесса. Если аргумент pid равен —1 и сигнал посылается из процесса привилегированного пользователя, то он бу- дет получен всеми процессами, за исключением процессов 0 и 1. Значение, возвращаемое этим системным вызовом, равно 0 при нормальном завер- шении и —1 при аварийном завершении. 325
Iink(name1 ,name2)char «namel ,*name2; Системный вызов link создает ссылку с именем, указанным с помощью аргу- мента пате2, на файл с именем, указанным с помощью аргумента namel. Значение, возвращаемое этим системным вызовом равно 0 при нормальном завершении и —1 при аварийном завершении. long lseek(fildes,offset,whence)long offset; Системный вызов Iseek перемещает указатель чтения-записи открытого ранее файла, пользовательский дескриптор файла которого задан с помощью аргу- мента fi Ides. Если аргумент whence равен 0, то указатель чтения-записи пере- мещается в позицию, заданную с помощью аргумента offset. Если аргумент whence равен 1, то значением указателя чтения-записи становится его теку- щее значение плюс значение аргумента offset. Если аргумент whence равен 2, то значением указателя чтения-записи становится размер файла плюс значе- ние аргумента offset. Значение, возвращаемое этим системным вызовом, равно старому значению указателя чтения-записи при нормальном заверше- нии выполнения или —1 при аварийном завершении. mknod(name,mode,addr)char «пате; Системный вызов mknod доступен только привилегированному пользовате- лю. В результате его выполнения создается новый файл с именем, заданным с помощью аргумента name, и кодом защиты файла, заданным с помощью аргумента mode. Код защиты файла может содержать признаки каталога, специального файла и т. д. Аргумент addr используется для спецификации типа специального файла. При создании обычного файла или каталога аргу- мент addr должен быть равен 0. Значение, возвращаемое этим системным вызовом, равно 0 при успешном завершении и —1 при аварийном заверше- нии. mount(special, name, rwf lag )char «special,«name; Системный вызов mount подключает том, заданный с помощью аргумента special, к файлу, имя которого задано с помощью аргумента name. Если аргу- мент rwf lag не равен 0, то для данного тома будет установлен режим "только чтение". Значение, возвращаемое этим системным вызовом, равно 0 при нормальном завершении и —1 при аварийном завершении. nice(incr) Системный вызов nice устанавливает текущему процессу новый приоритет, равный текущему приоритету плюс значение аргумента incr. Значение аргу- мента incr должно лежать в диапазоне от -20 до +20. Приращения, выходя- щие за границы этого диапазона, уменьшаются до граничных значений. Чем больше значение аргумента incr, тем ниже устанавливаемый приоритет. Отрицательное приращение может задавать только привилегированный пользователь. 326
open(name,mode)char «name; Системный вызов open открывает уже существующий файл, имя которого задано с помощью аргумента name. Значение аргумента mode определяет режим открытия: 0 — только чтение, 1 — только запись, 2 — чтение-запись. Значение, возвращаемое этим системным вызовом, равно пользовательско- му дескриптору файла при нормальном завершении и —1 при аварийном за- вершении. раи$е() Системный вызов pause приостанавливает выполнение текущего процесса до получения сигнала. pipe(fildes)int fildes[2]; Системный вызов pipe осуществляет создание канала. Массив tildes содер- жит пользовательские дескрипторы файлов: элемент fildes[0] — дескрип- тор входного файла, элемент fildes[1] — дескриптор выходного файла. При отсутствии процесса чтения процесс записи получит соответствующий сигнал. Значение, возвращаемое этим системным вызовом, равно 0 при нор- мальном завершении и —1 при аварийном завершении. prof Системный вызов profil используется для получения профиля времени выполнения. Более детальное описание зависит от конкретной реализации. ^include <signal.h> pt race (request, pid, addr.data) int *addr: Системный вызов ptrace используется при трассировке и отладке программ. Более детальное описание зависит от конкретной реализации. read(fildes,buf,bytes)char «buffer; При выполнении системного вызова read число байт, равное значению ар- гумента bytes, пересылается из открытого ранее файла с пользовательским дескриптором файла, заданным с помощью аргумента tildes, в буфер, адрес которого задан с помощью аргумента but. Аргумент bytes указывает макси- мальную длину. Реальный объем переданных с устройства (в основном с тер- минала} данных меньше. Значение, возвращаемое этим системным вызо- вом, равно 0 при обнаружении конца файла, —1 при аварийном завершении и реальному числу переданных байт при нормальном завершении. setuid(uid) setgid(gid) Перечисленные выше системные вызовы устанавливают идентификаторы пользователя или группы. Рядовой пользователь может изменять только 327
реальные идентификаторы. Привилегированный пользователь может изме- нять как реальные, так и действующие идентификаторы. Значения, возвра- щаемые этими системными вызовами, при нормальном завершении равны О, при аварийном завершении —1. «include <signal.h> (*signal(sig,func)()(*func)(); Значение, возвращаемое этим системным вызовом, представляет собой указатель на функцию. Аргумент func также является указателем на функ- цию. Если после выполнения системного вызова signal текущий процесс получит сигнал, номер которого указан с помощью аргумента sig, то прои- зойдет обращение к функции, адрес которой задан с помощью аргумента func, а номер сигнала будет передан ей в качестве аргумента. Подробности описаны в Руководстве. Значение, возвращаемое этим системным вызовом, равно предыдущему значению функции для данного сигнала или —1 в случае ошибки. ^include <sys/types.h> ^include <sys/stat.h> stat(name,buf)char »name;struct stat buf; fstat(fildes,buf)struct stat «buf; При выполнении этих системных вызовов в поле, заданное с помощью аргумента buf, копируется полная информация об описателе файла. При обращении к системному вызову stat аргумент name специфицирует имя файла. При обращении к системному вызову fstat аргумент fildes специ- фицирует пользовательский дескриптор файла. stime(tp)long *tp: Системный вызов stime, осуществляющий установку системного времени, доступен только привилегированному пользователю. Значение аргумента tp трактуется как число секунд, прошедшее с 00 ч 00 мин 00 с 1 января 1970 г. sync() Системный вызов sync осуществляет принудительное проведение отложен- ного вывода. Непривилегированные пользователи, как правило, не исполь- зуют этот системный вызов. long time(tloc)long *tloc; include <sys/types.h> include <sys/timeb.h> ftime(tp)struct timeb »tp; Системные вызовы time и ftime используются для получения системного времени, начальное значение которого было установлено с помощью систем- 328
ного вызова stime. В качестве своего значения системный вызов time возвращает текущее время в виде числа секунд, прошедшего от 00 ч 00 мин 00 с 1 января 1970 г. При этом если значение аргумента tloc отлично от 0, то аналогичный результат помещается по адресу, указанному с помощью аргумента tloc. Системный вызов ftime заполняет специальную структуру, описанную в файле timeb.h. Более подробная информация представлена в Руководстве. times(buffer)struct tbuffer «buffer; Системный вызов times используется для получения учетной информации об использовании процессом времени центрального процессора (ЦП). Струк- тура, указанная с помощью аргумента buffer, содержит четыре поля: proc_user_time — время использования ЦП в режиме пользователя, proc _system_time — время использования ЦП в режиме системы, chi Id _ user_time — суммарное время использования ЦП процессами- потомками в режиме пользователя, child_system_time — суммарное время использования ЦП процессами- потомками в режиме системы. umask(mask) Аргумент mask указывает, какие биты в коде защиты файла всех впослед- ствии создаваемых файлов должны быть равны 0. Системный вызов umask возвращает в качестве своего значения предыдущее значение аргумента mask. unlink(name)char «name; Системный вызов unlink используется для удаления входа в каталог. Если при этом была удалена последняя ссылка на файл, то он будет удален после того, как последний процесс, в котором он был открыт, закроет его. Значе- ние, возвращаемое этим системным вызовом, равно 0 при нормальном завершении и —1 при аварийном завершении. Удалять каталоги может только привилегированный пользователь, при этом нельзя удалять непустые ката- логи. ^include <sys/types.h> utime(file,timep)char «file; time_t timep[2]; Системный вызов utime используется для установки времени последнего обращения к файлу и времени последней модификации файла. Требуемые значения должны в указанном порядке содержаться в массиве timep. Сис- темный вызов utime может использоваться владельцем файла или привиле- гированным пользователем. Значение, возвращаемое этим системным вызо- вом, равно 0 при нормальном завершении и —1 при аварийном завершении. 329
wait(status)int «status; Системный вызов wait возвратит управление текущему процессу после того, как завертится один из его процессов-потомков, при этом значение, возвращаемое этим системным вызовом будет равно идентификатору завер- шившегося процесса. Переменная, указанная с помощью аргумента status, будет содержать код завершения, старший байт которого устанавливается системой, а младший байт может содержать номер сигнала, из-за которого было прервано выполнение процесса. Если при этом был произведен дамп оперативной памяти, то номер сигнала комбинируется с помощью операции логического ИЛИ с константой 0200. Если процесс не имеет процессов- потомков, то значение, возвращаемое этим системным вызовом, будет равно1. write(fildes,buffer,nbytes)char «buffer; Данный системный вызов работает аналогично системному вызову read, за исключением того, что осуществляет запись, а не чтение. При нормальном за- вершении этого системного вызова в указанный файл будет записано число байт, указанное с помощью аргумента nbytes. При аварийном завершении в случае ошибки значение, возвращаемое этим системным вызовом, будет рав- но —1. ОШИБКИ Ниже представлен краткий перечень ошибок, возникающих при выполнен нии системных вызовов. Вместе с кодом ошибки в нем Приводится стан- дартное имя ошибки, определенное в файле /usr/include/errno.h. При воз- никновении ошибки внешняя переменная еггпо принимает значение, равное соответствующему коду возникшей ошибки, которое может быть исполь- зовано в дальнейшем. Код ошибки Стандартное имя Что означает 0 отсутствует ничего 1 EPPERM пользователь не имеет соответствующих пол- номочий 2 ENOENT файл не найден 3 ESRCH нет такого процесса 4 EINTR выполнение системного вызова прервано по получению сигнала 5 ЕЮ ошибка ввода-вывода 6 ENXIO нет такого устройства ввода-вывода 7 E2BIG при обращении к системному вызову ехес указано слишком много аргументов 8 ENOEXEC некорректный формат выполняемого файла 9 EBADF некорректный пользовательский дескриптор файла 10 ECHILD отсутствуют процессы-потомки 330
Код ошибки Стандартное имя Что означает 11 Е AGAIN создать процесс невозможно 12 ENOMEM доступен слишком маленький объем памяти 13 EACCESS нарушение полномочий доступа к файлу 14 EFAULT ошибка в адресе 15 ENOTBLX ошибка в типе устройства 16 EBUSY устройство уже используется 17 EEXIST создаваемый файл уже существует 18 EXDEV попытка установить ссыпку на файл на дру- гом томе 19 ENODEV некорректное обращение к устройству 20 ENOTDIR требуется имя каталога 21 EISDIR не требуется имя каталога 22 EINVAL неправильный аргумент 23 ENFILE переполнение системной таблицы открытых файлов 24 EMFILE превышение допустимого числа пользова- тельских дескрипторов файлов 25 ENOTTY данное устройство не является терминалом 26 ETXTBSY файл занят — нельзя выполнить запись или вызвать на выполнение 27 EFBIG превышение допустимых размеров файла 28 ENOSPC на диске нет свободного места 29 ESPIPE попытка изменить значение указателя чтения- записи для канала 30 EROFS запись на диск запрещена 31 EMLINK слишком много ссылок на файл 32 EPIPE запись в канал, из которого не происходит чтения 33 EDOM ошибочное значение аргумента 34 ERANGE результат не может быть получен Последние две ошибки возникают не при обращении к системным вызо- вам,апри выполнении библиотечных функций для вычислений с плавающей точкой и упомянуты здесь для того, чтобы список ошибок был полным. СИГНАЛЫ Ниже приведены номера и названия всех сигналов, определенных для ОС UNIX на ЭВМ семейства PDP-11. В системах, не ориентированных на ЭВМ семейства PDP-11, этот список может быть другим, особенно в отношении сигналов SIGIOT и SIGEMT, посылка которых осуществляется в результате выполнения машинных команд, специфических для ЭВМ семейства PDP-11. Файл /usr/include/signat.h содержит названия сигналов и все необходимые для использования библиотечной функции signal директивы #define. 331
Номер сигнала Имя Причина 1 SIGHUP отмена 2 felGINT прерывание 3 SIGQUIT нестандартный выход 4 SIGILL неверная команда 5 SIGTRAP ловушка 6 SIGIOT прерывание ввода-вывода 7 SIGEMT прерывание при выполнении команды 8 SIGFPE исключительная ситуация при выполнении операций с плавающей точкой 9 SIGKILL уничтожение процесса 10 SIGBUS ошибка шины 11 SIGSEGV нарушение сегментации 12 SIGSYS неверный системный вызов 13 SIGPIPE запись в канал без чтения из него 14 SIGALRM будильник Для перехвата любого сигнала, за исключением 4 и 5, способ обработки должен переопределяться всякий раз при его получении. Если один из сигна- лов 3, 4, 5, 6, 7, 8, 10, 11 или 12 будет получен процессом, в котором для него не переопределен способ обработки, то будет проведен дамп оператив- ной памяти этого процесса. Способ обработки сигнала 9 не может быть пере- определен, и единственным надежным способом удалить процесс является посылка ему этого сигнала. Внутри операционной системы предусмотрена обработка не более 16 сиг- налов, однако допускается самостоятельная обработка большего числа сигналов. ПРИЛОЖЕНИЕ Е. ПРОГРАММА ovp Как правило, исходный текст программы, написанной на языке програм- мирования Си, редко перегружают комментариями. В частности, если объем такой программы сравним с объемом программы ovp, то смысл и порядок ее работы вполне понятны вовсе без комментариев. Обычно программа, исходный текст которой написан на языке программирования Си, может быть легко разделена на сравнительно небольшие по объему функции, каж- дая из которых решает одну-единственную задачу. Если Вам известно, какую задачу решает каждая из таких функций, то для Вас не составит большого труда понять и логику работы всей программы даже без знания того, что же происходит внутри каждой отдельной функции. Воспользовавшись приведенным рассуждением, мы сознательно не стали перегружать исходный текст программы ovp комментариями. В результате внешний вид его мало чем отличается от внешнего вида исходного текста любой пользовательской программы, написанной на языке программирова- ния Си. А это означает, что, отказавшись во имя типичности приводимого 332
примера от возможности снабдить исходный текст программы обильными комментариями и в то же время желая описать логику работы программы достаточно полно, мы вынесли все поясняющие замечания в это весьма небольшое предисловие к приложению Е. Глобальная задача, решаемая программой ovp, достаточно проста: после обработки значений всех флаговых аргументов (строки 37—54) открывают- ся файлы, имена которых указаны среди аргументов прог[«ммь1, и из ука- занных файлов вводится информация, которая затем подвергается некото- рой обработке. Если среди аргументов программы ovp отсутствуют имена файлов, то ввод информации осуществляется со стандартного ввода, при этом программа ovp может быть использована в качестве фильтра. Для обработки каждого открываемого файла функция main () обращает- ся к функции handle (), которая осуществляет ввод и обработку символов этого файла. При этом функция handle!) опознает и обрабатывает сим- волы newline (\п) и formfeed (\f), а также преобразует символы tab (\t) в требуемое число символов (строка 3). При обнаружении конца файла обработка заканчивается, В процессе обработки большинство символов непосредственно передается функции out(), которая осуществляет запол- нение массивов charsi[ ], ovrstkf ] и вычисляет номер позиции, выходящей за пределы текущей строки. Если в массивах, используемых для запоминания символов, есть свобод- ное место, то в функции out() выполняется одно из следующих трех дей- ствий; если текущим символом является символ backspace (\b), то счетчик текущей позиции уменьшается на единицу, если текущим символом является символ _, то отмечается последний символ текущей строки, а в случае любого другого значения текущего символа он записывается в массив chars[ j, В функции handle!} символы newline (\п) и formfeed (\f) восприни- маются как признаки окончания строки. При их обнаружении функция handle!) начинает поэлементный просмотр массивов chars[ ] и ovrstk( j (строка 105). Если очередной символ не отмечен в массиве ovrstk[ ], то он просто передается для печати функции spit(). Если же очередной символ отмечен в массиве ovstk[ ], то он печатается полужирным за счет наложения путем многократного вывода следующей последовательности символов: текущий символ, символ backspace (\b). Цикл в строках 116—118 опре- деляет число наложений. Функция spit!) выводит символы на стандартный вывод, задерживая при этом вывод пробелов, поскольку их может быть удастся частично преоб- разовать в последовательность символов tab {\t). Для этого в переменной outcol отслеживается номер текущей позиции. Наиболее трудным для понимания местом программы является после- довательность строк 37—73, где производится обработка управляющих параметров программы. Запись в строке 37 для новичка является просто откровением, поскольку в ней он обнаружит, что переменная argv была объявлена как указатель именно для того, чтобы ее можно было использо- 333
вать в качества массива. Фактически переменная *(argv+1) эквивалентна переменной argv [1]. Это означает, что для обращения к произвольному элементу массива значение переменной argv должно быть соответствующим образом увеличено. При вызове программы на выполнение ее параметры становятся доступны как элементы массива argv (argv [0] — для первого параметра, argv [1] — для второго и т. д.). Имя программы всегда является первым аргументом, следовательно, начиная с элемента argv [1 ], распопа* гаются аргументы, заданные пользователем и представленные в виде указа- телей на символьные строки. Оператор присваивания ср = argv [11; устанавливает значение указателя ср равным адресу первого байта первого аргумента. Если программа была вызвана на выполнение с помощью команды ovp —t ххххх то переменная ср будет содержать адрес строки —t, а значением «ср будет символ —. Для того чтобы понять, как в программе ovp обрабатываются флаги, необходимо внимательно просмотреть строки 37, 39—47. Доступ к аргументам осуществляется в цикле, в процессе выполнения которого значение переменной argc уменьшается, а указатель argv перево- дится на следующий аргумент всякий раз после того, как был обработан очередной флаг. Подобный алгоритм положен в основу работы большинства функций tnainO в программах, написанных на языке программирования Си. 1 /« 2 я Программ! ovp Обеспечивает преобразование символов, выделенных 3 и с номоцьи си не опа по дчвр кяввння, в символы, выделенные пвча тьн с 4 и неложением. В программе ovp преиполагается, что стопоры табуляции 5 в установлены не каждой восьмой позиции; прог ранчо не работает я 6 * други* условия* . Переменное NUNDE.R определяет максимальное число 7 и наложений; значение, большее 4, использовать не рекомендуется. 8 * Гели задан аргумент -on, то будет выполнена печать с п 9 и наложениями . Если задан аргумент -т , то символ табуляция 10 я игнорируется и/ 11 12 (♦include < s tdio . h > /* Вклмчить объявления стандартной ькблмат* »ки 13 14 ♦define LIKES1Z 512 /« ввода-вывода Максимальна» мисло символов в строке «/ */ 15 ♦define HUNGER 4 /« Числа наложений по умолчании »/ 16 17 FILE «inf Z* Указатель не входной fain а/ 18 19 char charsILINESIZ1,ovrstkILIHESIZ1; 20 int posn; /» Текуча* позиции в тексте и/ 21 int outcol; /» Указатель на следумц*А символ а/ 22 int spaces; /fl Используется *ля интерпретации символов 23 и табуляции »/ 24 int nover HUHDER /» Начальное значение переменной never »/ 25 int tflag /* Признак необходимости интерпретации 26 s символов табуляции »/ 27 28 ftairH arqc,arg и) int argcj char »*argvj< 334
29 30 31 32 33 34 35 36 37 38 39 40 41 4? 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 5В 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 9l 92 93 * 1)61мл9ние переменной argv, представленное в нее, является * достаточным дли того, чтобы обрабатыаать ее как насек», я поскольку языке программирования Си масс кап и указатели и очень тесно связаны int с; register char *ср, uhiletargc ) 1 66 step = argutll) == arg<--; argon-; switch(»++cp 1C case 'o'; nover - atoitt+cp); iffnover (Oil nover > NU№ERH fprjntf(etderr,"Ovp; bad overprint argи негt\n"1; n over = NllNDER ; } break ; < ase 'т ' : T flag t a ; break; default ; fprintftstderr,"Ovp: bad f lagXn " 1; > > /* (ели задан то ль ко один.аргумент - имя программы, то * необюднмо осуществлять ееод со стандартного ввода 4/ if large == t>C inf = Stdin; handiet), eiit(0 1; forte = 1; c < argc, c+*> tfttinf = fopen(argvEc],"r">) -< NULL) fprintf<stderr,"Ovpi can't openXsXn".argvtc11; el set handiet 1; fclosetinf >; } handiet IE register int i; Int nunder,eol; int c; fort;;)C /Я Вывод строки; если необходимо, печатать с напоменяем 4/ ford = 0; i < posn, 1++)С charsEil = 0; ovrst k E i I * 0 ; 1 eol * 0; posn =0; dot С ч getc(inf 1, switchtc1C case EOF. ret urn; 335
94 9Б 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 156 case 'Xt' do с и t<' ') j whi 1 e<pdsn 6 07) , break > case 'Xti' case 'Xf' out('\n'); eolt+> for(i * 0; I < posn; i + + ) if(ovrstkE1])C if(chars t1J OK chars(i 1 r ' * > ovrstk Ill = 0; > > ford = 0, i < роэп, 1 + +JC spit(chars[iJ) ? if< ovrstki i 1 > for<runder = 0, nunder < noverj nunder++)C spit<'Xb'); spittcharstil) j > break > defa и 11 out CO; iwh 11 e Ce ol 0 > , о и t(с ) char с, С /* * Если страха слиткам длимная> то обрезать ее, * не выдавая сообщения об ошибке 4/ if( posn >= LINESIZ 44 с 'Xb'H chars[LINESIZ-1] = '\n'; о arstktLINESIZ-i1 = 0; ret urn j switch(с >C case '\b' if t posn ) posn--j break> case J ' о vrst k[p osn1 = 1; p osn + +; break; def a и 11 chars[p osn] = c> p osn ++ > brea k; 336
159 160 161 162 163 164 165 166 167 16В 169 170 171 172 173 174 175 176 177 178 17? 180 IS! 182 183 184 185 186 187 188 spit(c > char c , C lot targetj /t Накапливать пробели для преобразования в символ табуляции */ iffc == ' tflag -= 0)С sp aces++ j ret urn > 3 if(с *- '\nJ) outcol 5 -lj /* Переменная будет увеличена до нуля позднее >/ elset target = outcol +• spaces, while<(<outcal * g) ( '"07) <= target )C putc < '\t' ,stdout > , outcol = (outcol * 8) & ~07j } whiletoutcol < target>C о и tc q1++) p и t c <' J,stdout)) 3 } spaces = 0) if(c == 'Xb' 64 outcol) outcol--; else ou tcol + + ) pиtc <c^stdout ) }
ОТВЕТЫ К УПРАЖНЕНИЯМ 1. ГЛАВА 1 1. Мы не можем ответить Вам на этот вопрос. Вы должны сами выяснить, каким клавишам соответствуют специальные символы в Вашей версии ОС UNIX по имею- щейся у Вас документации. 2. Команда представляет собой программу, выполняющуюся на ОВМ. Аргументы, как правило, анализируются командой, С помощью аргументов программе передается информация, используемая командой во время ее выполнения. В большинстве случаев аргументы команд представляют собой последовательность имен файлов. 3. Аргументы отделяются друг от друга и от имени команды с помощью символов или tab. 4. Завершить выполнение программы в этом случае Вы можете нажатием клавиши, соответствующей специальному символу INTERRUPT. 5. Интерпретатор команд shell представляет собой программу, обеспечивающую интерпретацию командного языка ОС UNIX — языка программирования shell. Интер- претатор команд shell в принципе ничем не отличается от тех программ, которые пишите Вы сами, за исключением того, что в нем, по-видимому, не содержится ошибок. Если Вам очень захочется, то Вы можете сами написать свой собственный интерпретатор команд. 6. Выйти из ОС UNIX можно в результате нажатия клавиши, соответствующей специальному символу EOT, в ответ на выведенный на терминал промптер интерпрета- тора команд shell. Выходить из ОС UNIX после завершения сеанса работы необходимо потому, что в противном случае Вы можете встретиться с различными неприятностями, такими, например, как удаление Ваших файлов кем-то. 2. ГЛАВА 2 1. Максимальная длина имени файла составляет 14 символов. 2. Основной каталог пользователя - это тот каталог, который является текущим сразу после входа пользователя в ОС UNIX. 3. Теоретически число Ваших файлов (а каталог является файлом) ничем не ограни- чено. К сожалению, ни один файл не может превышать по размеру емкость диска, что естественно ограничивает число Ваших файлов. 4. Является файл каталогом или обычным файлом. Вы можете выяснить с помощью команды Is —I, которая в результате своего выполнения выводит на терминал для каталогов первым символ d, а для обычных файлов — символ —. 5. В ОС UNIX различается три типа доступа к файлам: доступ по чтению, доступ по записи и доступ по выполнению. Защита файлов по каждому из этих трех типов доступа к файлам обеспечивается отдельно для трех различаемых по отношению к каждому файлу категорий пользователей — владельца, членов группы и прочих поль- зователей. 6. Группа — это несколько пользователей, которые хотят разделять между собой некоторый набор файлов (т. е. осуществлять совместный доступ к этому набору фай- лов) . Обычно группа — это коллектив, работающий над общим проектом. 7. По умолчанию стандартный ввод-вывод назначен на терминал. 8. Метасимволы — это символы, имеющие специальный смысл для интерпретатора команд shell. Некоторые из метасимволов используются для сокращенной записи имен файлов. В этом случае осуществляется генерация имен файлов. 338
9. Замкнуть стандартный вывод одной команды на стандартный ввод другой коман- ды Вы можете с помощью конвейера. Для создания конвейера используется символ | , что иллюстрируется следующим примером: команда! I команда? 3. ГЛАВА 5 1. Полное имя файла — это путь по дереву. Абсолютное полное имя файла специ- фицирует путь от корня дерева до файла. Относительное полное имя файла специфици- рует путь от текущего каталога до файла. 2. Файловая система ОС UNIX имеет иерархическую древовидную структуру. 3. Описатель файла содержит информацию о файле, такую, например, как код заши- ты файла и указатели на местоположение блоков файла на съемном пакете дисков. Структура описателя файла не зависит от типа файла, однако информация, содержа- щаяся в описателе файла, трактуется ОС UNIX по-разному, в зависимости от типа файла. 4. Специальный файл — это файл, соответствующий внешнему устройству, например АЦПУ или накопителю на магнитных дисках. 5. Для того чтобы обеспечить возможность работы со специальными файлами, вовсе не требуется модифицировать Вашу программу, поскольку одни и те же системные вызовы используются для проведения операций ввода-вывода независимо от типа файла.
СПИСОК ЛИТЕРАТУРЫ [1 ] Fundamentals of Operating Systems, A.M,Lister, Macmillan, 1981, [2] Cooperating sequential processes, E.W.Dijkstra, in Programming Languages, Academic Press, 1965 [3] A Introduction to the UNIX Text Editor, Brian Kernighan, found in the UNIX Programmer's Manual, volume 2a, Bell Laboratories, 1979. [4] Advanced Editing on UNIX, as [3]. [5] Rhestr О Enwau Lleodd, Davies, Gwasg Prifysgol Cymru, 1967. [6] The C programming language, Kernighan & Ritchie, Prentice-Hall, 1978. [7] The C Reference Manual, Dennis Ritchie, as [3]. [8] Penguin English Dictionary, Second Edition, Penguin, 1974. [9] The Nroff/Troff User's Manual, Joseph. F. Ossanna, as [3]. СПИСОК РАБОТ, ПЕРЕВЕДЕННЫХ НА РУССКИЙ ЯЗЫК 2. Дийкстра Э. Взаимодействие последовательных процессов. Языки про- граммирования: Пер. с англ. — М.: Мир, 1972, с. 9 — 86. 6. Керниган Б., Ритчи Д., Фью эр А. Язык программирования Си. Задачи по языку Си: Пер, с англ. — М.: Финансы и статистика, 1985, с. 10 — 192.
ОГЛАВЛЕНИЕ Предисловие к русскому изданию........................................ 5 Предисловие авторов................................................... 8 Глава 1. Первые шаги................................'................ 11 Глава 2. Файлы и простые команды...................................... 26 Язык программирования shell................................... 46 Глава 3, Редактор текстов ОС UNIX.................................... 63 Глава 4. Язык программирования Си..................................... 84 Глава 5. Файловая система ОС UNIX....................................116 Глава 6. Инструментальные средства ОС UNIX...........................129 Глава 7. Подготовка документации в ОС UNIX...........................145 Глава 3. Процессы и системные вызовы.................................169 Глава 9. Библиотеки ОС UNIX..........................................195 Глава 10. Сопровождение ОС UNIX......................................219 Приложение А.........................................................247 Приложение Б.........................................................274 Приложение В.........................................................281 Приложение Г.........................................................293 Приложение Д.........................................................322 Приложение Е.........................................................332 Ответь, к упражнениям................................................338 Список литературы....................................................340
МАЙК БАНАХАМ, ЭНДИ РАТТЕР ВВЕДЕНИЕ В ОПЕРАЦИОННУЮ СИСТЕМУ UNIX Заведующая редакцией О. В. Толкачева Редактор М. С. Гэрбон Художественный редактор Т. В. Бусарова Художник В. А. Козлов Технический редактор Л. А. Горшкова Корректор 3, Г. Галушкина ИБИ" 981 Подписано в печать 26.11.85 Формат 60X84/16 Бумага кн.журн.им. Гарнитура "Универе" Печать офсетная Усл. печ. л. 20,0 Усл. кр.-отт. 20,0 Уч.-изд. л. 21,4 Тираж 15000 зкз. Изд. № 21029 Зак. №1165 Цена 1 р. 90 к. Издательство "Радио и связь". 101000, Москва, Почтамт,а/я 693 Московская типография № 4 Союэпопиграфпрома при Государственном комитете СССР по делам издательств, полиграфии и книжной торговли. 129041, Москва, Б. Переяс- лавская ул., д. 46
УВАЖАЕМЫЙ ЧИТАТЕЛЬ! В 1986 г. в издательстве “Радио и связь" выйдут в свет книги: Ш и л е й к о А. В., Ш и л ей к о Т. И. Микропроцессоры. — 11 л., ил. — 40 к. Излагаются основные идеи, положенные в основу принципа дей- ствия микропроцессоров, типы, разновидности и их рабочие характе- ристики. Обсуждаются особенности применения микропроцессоров в бытовой аппаратуре, в системах управления промышленными и транспортными объектами и в системах связи. Приводятся конструк- ции микрокалькуляторов, интеллектуальных терминалов и микро- ЭВМ. Для широкого круга читателей, интересующихся применением микропроцессоров в различных областях. Зайцев В. Ф., Быков В. А. Кодирование информации в ЕС ЭВМ. - 7 л., ил. — 40 к. Рассматриваются способы представления информации в различ- ных системах счисления, форматы чисел и обработка их в ЕС ЭВМ. Описываются системы кодирования информации: перфокарточный код (КПК-12), двоичный код обработки информации- (ДКОИ), вось- мибитный и семибитный коды для обмена информацией (КОИ-8 и КОИ-7). Приводятся иллюстрации, диаграммы, таблицы и примеры, содержащие сведения, необходимые программистам различной квали- фикации. Для инженерно-технических работников, связанных с эксплуата- цией ЭВМ, и программистов. Л и п а е в В. В. Тестирование программ. — 20 л., ил. — 1 р. 40 к. Рассматриваются методы тестирования программных модулей и сложных комплексов программ. Анализируются эффективность ме- тодов, а также средства, автоматизирующие процесс тестирования. Даются рекомендации по их эффективному применению на разных этапах проектирования и сопровождения комплексов программ. Для инженерно-технических работников, занимающихся проекти- рованием и эксплуатацией программного обеспечения. Полезна сту- дентам вузов. 343
М я ч е в А. А., Иванов В. В. Интерфейсы вычислительных си- стем на базе мини- и микро-ЭВМ/ Под ред. Б. Н. Наумова. — 19 л., ил., — 1 р. 20 к. Изложены основы организации, функционирования, классифика- ции, анализа, методов выбора внутрисистемных и межсистемных ин- терфейсов сосредоточенных, локальных и распределенных вычисли- тельных систем. Особое внимание уделено рассмотрению малых ин- терфейсов периферийных и внешних запоминающих устройств. При- ведены унифицированные и перспективные решения по интерфейсам и протоколам, в том числе для магистральных мультипроцессор- ных систем. Для инженерно-технических работников, занимающихся разработ- кой и использованием средств вычислительной техники. Приобрести эти книги Вы можете в книжных магазинах, распро- страняющих научно-техническую литературу.